1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2016 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 "qmakelibraryinfo.h"
6
7#include <qdir.h>
8#include <qfile.h>
9#include <qglobalstatic.h>
10#include <qsettings.h>
11#include <qscopedpointer.h>
12#include <qstringlist.h>
13#include <private/qlibraryinfo_p.h>
14
15#include <utility>
16
17QT_BEGIN_NAMESPACE
18
19struct QMakeLibrarySettings
20{
21 QMakeLibrarySettings() { load(); }
22 void load();
23
24 bool haveDevicePaths;
25 bool haveEffectiveSourcePaths;
26 bool haveEffectivePaths;
27 bool havePaths;
28};
29Q_GLOBAL_STATIC(QMakeLibrarySettings, qmake_library_settings)
30
31void QMakeLibrarySettings::load()
32{
33 QSettings *settings = QLibraryInfoPrivate::configuration();
34 if (settings) {
35 QStringList children = settings->childGroups();
36 haveDevicePaths = children.contains(str: QLatin1String("DevicePaths"));
37 haveEffectiveSourcePaths = children.contains(str: QLatin1String("EffectiveSourcePaths"));
38 haveEffectivePaths =
39 haveEffectiveSourcePaths || children.contains(str: QLatin1String("EffectivePaths"));
40 // Backwards compat: an existing but empty file is claimed to contain the Paths section.
41 havePaths = (!haveDevicePaths && !haveEffectivePaths
42 && !children.contains(str: QLatin1String("Platforms")))
43 || children.contains(str: QLatin1String("Paths"));
44 } else {
45 haveDevicePaths = false;
46 haveEffectiveSourcePaths = false;
47 haveEffectivePaths = false;
48 havePaths = false;
49 }
50}
51
52void QMakeLibraryInfo::reload()
53{
54 QLibraryInfoPrivate::reload();
55 if (qmake_library_settings.exists())
56 qmake_library_settings->load();
57}
58
59bool QMakeLibraryInfo::haveGroup(PathGroup group)
60{
61 QMakeLibrarySettings *ls = qmake_library_settings();
62 return ls
63 && (group == EffectiveSourcePaths ? ls->haveEffectiveSourcePaths
64 : group == EffectivePaths ? ls->haveEffectivePaths
65 : group == DevicePaths ? ls->haveDevicePaths
66 : ls->havePaths);
67}
68
69void QMakeLibraryInfo::sysrootify(QString &path)
70{
71 // Acceptable values for SysrootifyPrefixPath are "true" and "false"
72 if (!QVariant::fromValue(value: rawLocation(loc: SysrootifyPrefixPath, group: FinalPaths)).toBool())
73 return;
74
75 const QString sysroot = rawLocation(loc: SysrootPath, group: FinalPaths);
76 if (sysroot.isEmpty())
77 return;
78
79 if (path.size() > 2 && path.at(i: 1) == QLatin1Char(':')
80 && (path.at(i: 2) == QLatin1Char('/') || path.at(i: 2) == QLatin1Char('\\'))) {
81 path.replace(i: 0, len: 2, after: sysroot); // Strip out the drive on Windows targets
82 } else {
83 path.prepend(s: sysroot);
84 }
85}
86
87QString QMakeLibraryInfo::path(int loc)
88{
89 QString ret = rawLocation(loc, group: QMakeLibraryInfo::FinalPaths);
90
91 // Automatically prepend the sysroot to target paths
92 if (loc < QMakeLibraryInfo::FirstHostPath)
93 sysrootify(path&: ret);
94
95 return ret;
96}
97
98static QLibraryInfo::LibraryPath hostToTargetPathEnum(int loc)
99{
100 static std::pair<int, QLibraryInfo::LibraryPath> mapping[] = {
101 { QMakeLibraryInfo::HostBinariesPath, QLibraryInfo::BinariesPath },
102 { QMakeLibraryInfo::HostLibraryExecutablesPath, QLibraryInfo::LibraryExecutablesPath },
103 { QMakeLibraryInfo::HostLibrariesPath, QLibraryInfo::LibrariesPath },
104 { QMakeLibraryInfo::HostDataPath, QLibraryInfo::DataPath },
105 { QMakeLibraryInfo::HostPrefixPath, QLibraryInfo::PrefixPath }
106 };
107 for (size_t i = 0; i < sizeof(mapping) / sizeof(mapping[0]); ++i) {
108 if (mapping[i].first == loc)
109 return mapping[i].second;
110 }
111 qFatal(msg: "Unhandled host path %d in hostToTargetPathEnum.", loc);
112 Q_UNREACHABLE();
113}
114
115static QLibraryInfoPrivate::LocationInfo defaultLocationInfo(int loc)
116{
117 QLibraryInfoPrivate::LocationInfo result;
118
119 if (loc < QMakeLibraryInfo::FirstHostPath) {
120 result = QLibraryInfoPrivate::locationInfo(loc: static_cast<QLibraryInfo::LibraryPath>(loc));
121 } else if (loc <= QMakeLibraryInfo::LastHostPath) {
122 result = QLibraryInfoPrivate::locationInfo(loc: hostToTargetPathEnum(loc));
123 result.key.prepend(QStringLiteral("Host"));
124 } else if (loc == QMakeLibraryInfo::SysrootPath) {
125 result.key = QStringLiteral("Sysroot");
126 } else if (loc == QMakeLibraryInfo::SysrootifyPrefixPath) {
127 result.key = QStringLiteral("SysrootifyPrefix");
128 } else if (loc == QMakeLibraryInfo::TargetSpecPath) {
129 result.key = QStringLiteral("TargetSpec");
130 } else if (loc == QMakeLibraryInfo::HostSpecPath) {
131 result.key = QStringLiteral("HostSpec");
132 }
133 return result;
134}
135
136static QString libraryInfoPath(QLibraryInfo::LibraryPath location)
137{
138 return QLibraryInfoPrivate::path(p: location, usageMode: QLibraryInfoPrivate::UsedFromQtBinDir);
139}
140
141static QString storedPath(int loc)
142{
143 QString result;
144 if (loc < QMakeLibraryInfo::FirstHostPath) {
145 result = libraryInfoPath(location: static_cast<QLibraryInfo::LibraryPath>(loc));
146 } else if (loc <= QMakeLibraryInfo::LastHostPath) {
147 if (loc == QMakeLibraryInfo::HostDataPath) {
148 // Handle QT_HOST_DATADIR specially. It is not necessarily equal to QT_INSTALL_DATA.
149 result = QT_HOST_DATADIR;
150 } else {
151 result = libraryInfoPath(location: hostToTargetPathEnum(loc));
152 }
153 } else if (loc == QMakeLibraryInfo::SysrootPath) {
154 // empty result
155 } else if (loc == QMakeLibraryInfo::SysrootifyPrefixPath) {
156 result = QStringLiteral("false");
157 } else if (loc == QMakeLibraryInfo::TargetSpecPath) {
158 result = QT_TARGET_MKSPEC;
159 } else if (loc == QMakeLibraryInfo::HostSpecPath) {
160 result = QT_HOST_MKSPEC;
161 }
162 return result;
163}
164
165QString QMakeLibraryInfo::rawLocation(int loc, QMakeLibraryInfo::PathGroup group)
166{
167 QString ret;
168 bool fromConf = false;
169 // Logic for choosing the right data source: if EffectivePaths are requested
170 // and qt.conf with that section is present, use it, otherwise fall back to
171 // FinalPaths. For FinalPaths, use qt.conf if present and contains not only
172 // [EffectivePaths], otherwise fall back to builtins.
173 // EffectiveSourcePaths falls back to EffectivePaths.
174 // DevicePaths falls back to FinalPaths.
175 PathGroup orig_group = group;
176 if (QMakeLibraryInfo::haveGroup(group)
177 || (group == EffectiveSourcePaths
178 && (group = EffectivePaths, QMakeLibraryInfo::haveGroup(group)))
179 || ((group == EffectivePaths || group == DevicePaths)
180 && (group = FinalPaths, QMakeLibraryInfo::haveGroup(group)))
181 || (group = orig_group, false)) {
182 fromConf = true;
183
184 QLibraryInfoPrivate::LocationInfo locinfo = defaultLocationInfo(loc);
185 if (!locinfo.key.isNull()) {
186 QSettings *config = QLibraryInfoPrivate::configuration();
187 Q_ASSERT(config != nullptr);
188 config->beginGroup(prefix: QLatin1String(group == DevicePaths ? "DevicePaths"
189 : group == EffectiveSourcePaths
190 ? "EffectiveSourcePaths"
191 : group == EffectivePaths ? "EffectivePaths"
192 : "Paths"));
193
194 if (locinfo.fallbackKey.isNull()) {
195 ret = config->value(key: locinfo.key).toString();
196 } else {
197 QVariant v = config->value(key: locinfo.key);
198 if (!v.isValid())
199 v = config->value(key: locinfo.fallbackKey);
200 ret = v.toString();
201 }
202
203 if (ret.isEmpty()) {
204 if (loc == HostPrefixPath || loc == TargetSpecPath || loc == HostSpecPath
205 || loc == SysrootifyPrefixPath || loc == QLibraryInfo::PrefixPath) {
206 fromConf = false;
207 } else {
208 ret = locinfo.defaultValue;
209 }
210 // The last case here is SysrootPath, which can be legitimately empty.
211 // All other keys have non-empty fallbacks to start with.
212 }
213
214 // TODO: Might be replaced by common for qmake and qtcore function
215 int startIndex = 0;
216 forever {
217 startIndex = ret.indexOf(c: QLatin1Char('$'), from: startIndex);
218 if (startIndex < 0)
219 break;
220 if (ret.size() < startIndex + 3)
221 break;
222 if (ret.at(i: startIndex + 1) != QLatin1Char('(')) {
223 startIndex++;
224 continue;
225 }
226 int endIndex = ret.indexOf(c: QLatin1Char(')'), from: startIndex + 2);
227 if (endIndex < 0)
228 break;
229 auto envVarName =
230 QStringView { ret }.mid(pos: startIndex + 2, n: endIndex - startIndex - 2);
231 QString value =
232 QString::fromLocal8Bit(ba: qgetenv(varName: envVarName.toLocal8Bit().constData()));
233 ret.replace(i: startIndex, len: endIndex - startIndex + 1, after: value);
234 startIndex += value.size();
235 }
236 config->endGroup();
237
238 ret = QDir::fromNativeSeparators(pathName: ret);
239 }
240 }
241
242 if (!fromConf)
243 ret = storedPath(loc);
244
245 // These values aren't actually paths and thus need to be returned verbatim.
246 if (loc == TargetSpecPath || loc == HostSpecPath || loc == SysrootifyPrefixPath)
247 return ret;
248
249 if (!ret.isEmpty() && QDir::isRelativePath(path: ret)) {
250 QString baseDir;
251 if (loc == HostPrefixPath || loc == QLibraryInfo::PrefixPath || loc == SysrootPath) {
252 // We make the prefix/sysroot path absolute to the executable's directory.
253 // loc == PrefixPath while a sysroot is set would make no sense here.
254 // loc == SysrootPath only makes sense if qmake lives inside the sysroot itself.
255 QSettings *config = QLibraryInfoPrivate::configuration();
256 if (config != nullptr) {
257 baseDir = QFileInfo(config->fileName()).absolutePath();
258 }
259 } else if (loc >= FirstHostPath && loc <= LastHostPath) {
260 // We make any other host path absolute to the host prefix directory.
261 baseDir = rawLocation(loc: HostPrefixPath, group);
262 } else {
263 // we make any other path absolute to the prefix directory
264 baseDir = rawLocation(loc: QLibraryInfo::PrefixPath, group);
265 if (group == EffectivePaths)
266 sysrootify(path&: baseDir);
267 }
268 ret = QDir::cleanPath(path: baseDir + QLatin1Char('/') + ret);
269 }
270 return ret;
271}
272
273QT_END_NAMESPACE
274

source code of qtbase/qmake/qmakelibraryinfo.cpp