1 /****************************************************************************
2 **
3 ** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qbs.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "pkgconfigjs.h"
41
42 #include <language/scriptengine.h>
43 #include <tools/version.h>
44
45 #include <QtScript/qscriptengine.h>
46 #include <QtScript/qscriptvalue.h>
47
48 #include <QtCore/QProcessEnvironment>
49
50 #include <stdexcept>
51
52 namespace qbs {
53 namespace Internal {
54
55 namespace {
56
convert(const C & c,F && f)57 template<typename C, typename F> QVariantList convert(const C &c, F &&f)
58 {
59 QVariantList result;
60 result.reserve(int(c.size()));
61 std::transform(c.begin(), c.end(), std::back_inserter(result), f);
62 return result;
63 }
64
variablesMapToMap(const PcPackage::VariablesMap & variables)65 QVariantMap variablesMapToMap(const PcPackage::VariablesMap &variables)
66 {
67 QVariantMap result;
68 for (const auto &item: variables)
69 result.insert(QString::fromStdString(item.first), QString::fromStdString(item.second));
70 return result;
71 }
72
packageToMap(const PcPackage & package)73 QVariantMap packageToMap(const PcPackage &package)
74 {
75 QVariantMap result;
76 result[QStringLiteral("isBroken")] = false;
77 result[QStringLiteral("filePath")] = QString::fromStdString(package.filePath);
78 result[QStringLiteral("baseFileName")] = QString::fromStdString(package.baseFileName);
79 result[QStringLiteral("name")] = QString::fromStdString(package.name);
80 result[QStringLiteral("version")] = QString::fromStdString(package.version);
81 result[QStringLiteral("description")] = QString::fromStdString(package.description);
82 result[QStringLiteral("url")] = QString::fromStdString(package.url);
83
84 const auto flagToMap = [](const PcPackage::Flag &flag)
85 {
86 QVariantMap result;
87 const auto value = QString::fromStdString(flag.value);
88 result[QStringLiteral("type")] = QVariant::fromValue(qint32(flag.type));
89 result[QStringLiteral("value")] = value;
90 return result;
91 };
92
93 const auto requiredVersionToMap = [](const PcPackage::RequiredVersion &version)
94 {
95 using Type = PcPackage::RequiredVersion::ComparisonType;
96 QVariantMap result;
97 result[QStringLiteral("name")] = QString::fromStdString(version.name);
98 const auto versionString = QString::fromStdString(version.version);
99 const auto qbsVersion = Version::fromString(QString::fromStdString(version.version));
100 const auto nextQbsVersion = Version(
101 qbsVersion.majorVersion(),
102 qbsVersion.minorVersion(),
103 qbsVersion.patchLevel() + 1);
104 switch (version.comparison) {
105 case Type::LessThan:
106 result[QStringLiteral("versionBelow")] = versionString;
107 break;
108 case Type::GreaterThan:
109 result[QStringLiteral("versionAtLeast")] = nextQbsVersion.toString();
110 break;
111 case Type::LessThanEqual:
112 result[QStringLiteral("versionBelow")] = nextQbsVersion.toString();
113 break;
114 case Type::GreaterThanEqual:
115 result[QStringLiteral("versionAtLeast")] = versionString;
116 break;
117 case Type::Equal:
118 result[QStringLiteral("version")] = versionString;
119 break;
120 case Type::NotEqual:
121 result[QStringLiteral("versionBelow")] = versionString;
122 result[QStringLiteral("versionAtLeast")] = nextQbsVersion.toString();
123 break;
124 case Type::AlwaysMatch:
125 break;
126 }
127 result[QStringLiteral("comparison")] = QVariant::fromValue(qint32(version.comparison));
128 return result;
129 };
130
131 result[QStringLiteral("libs")] = convert(package.libs, flagToMap);
132 result[QStringLiteral("libsPrivate")] = convert(package.libsPrivate, flagToMap);
133 result[QStringLiteral("cflags")] = convert(package.cflags, flagToMap);
134 result[QStringLiteral("requires")] = convert(package.requiresPublic, requiredVersionToMap);
135 result[QStringLiteral("requiresPrivate")] =
136 convert(package.requiresPrivate, requiredVersionToMap);
137 result[QStringLiteral("conflicts")] = convert(package.conflicts, requiredVersionToMap);
138 result[QStringLiteral("variables")] = variablesMapToMap(package.variables);
139
140 return result;
141 };
142
brokenPackageToMap(const PcBrokenPackage & package)143 QVariantMap brokenPackageToMap(const PcBrokenPackage &package)
144 {
145 QVariantMap result;
146 result[QStringLiteral("isBroken")] = true;
147 result[QStringLiteral("filePath")] = QString::fromStdString(package.filePath);
148 result[QStringLiteral("baseFileName")] = QString::fromStdString(package.baseFileName);
149 result[QStringLiteral("errorText")] = QString::fromStdString(package.errorText);
150 return result;
151 }
152
packageVariantToMap(const PcPackageVariant & package)153 QVariantMap packageVariantToMap(const PcPackageVariant &package)
154 {
155 return package.visit([](const auto &value) {
156 using T = std::decay_t<decltype(value)>;
157 if constexpr (std::is_same_v<T, PcPackage>)
158 return packageToMap(value);
159 else
160 return brokenPackageToMap(value);
161 });
162 }
163
envToVariablesMap(const QProcessEnvironment & env)164 PcPackage::VariablesMap envToVariablesMap(const QProcessEnvironment &env)
165 {
166 PcPackage::VariablesMap result;
167 const auto keys = env.keys();
168 for (const auto &key : keys)
169 result[key.toStdString()] = env.value(key).toStdString();
170 return result;
171 }
172
variablesFromQVariantMap(const QVariantMap & map)173 PcPackage::VariablesMap variablesFromQVariantMap(const QVariantMap &map)
174 {
175 PcPackage::VariablesMap result;
176 for (auto it = map.cbegin(), end = map.cend(); it != end; ++it)
177 result[it.key().toStdString()] = it.value().toString().toStdString();
178 return result;
179 }
180
stringListToStdVector(const QStringList & list)181 std::vector<std::string> stringListToStdVector(const QStringList &list)
182 {
183 std::vector<std::string> result;
184 result.reserve(list.size());
185 for (const auto &string : list)
186 result.push_back(string.toStdString());
187 return result;
188 }
189
190 } // namespace
191
ctor(QScriptContext * context,QScriptEngine * engine)192 QScriptValue PkgConfigJs::ctor(QScriptContext *context, QScriptEngine *engine)
193 {
194 try {
195 PkgConfigJs *e = nullptr;
196 switch (context->argumentCount()) {
197 case 0:
198 e = new PkgConfigJs(context, engine);
199 break;
200 case 1:
201 e = new PkgConfigJs(context, engine, context->argument(0).toVariant().toMap());
202 break;
203
204 default:
205 return context->throwError(
206 QStringLiteral("TextFile constructor takes at most three parameters."));
207 }
208
209 return engine->newQObject(e, QScriptEngine::ScriptOwnership);
210 } catch (const PcException &e) {
211 return context->throwError(QString::fromUtf8(e.what()));
212 }
213 }
214
PkgConfigJs(QScriptContext * context,QScriptEngine * engine,const QVariantMap & options)215 PkgConfigJs::PkgConfigJs(
216 QScriptContext *context, QScriptEngine *engine, const QVariantMap &options) :
217 m_pkgConfig(std::make_unique<PkgConfig>(
218 convertOptions(static_cast<ScriptEngine *>(engine)->environment(), options)))
219 {
220 Q_UNUSED(context);
221 for (const auto &package : m_pkgConfig->packages()) {
222 m_packages.insert(
223 QString::fromStdString(package.getBaseFileName()), packageVariantToMap(package));
224 }
225 }
226
convertOptions(const QProcessEnvironment & env,const QVariantMap & map)227 PkgConfig::Options PkgConfigJs::convertOptions(const QProcessEnvironment &env, const QVariantMap &map)
228 {
229 PkgConfig::Options result;
230 result.libDirs =
231 stringListToStdVector(map.value(QStringLiteral("libDirs")).toStringList());
232 result.extraPaths =
233 stringListToStdVector(map.value(QStringLiteral("extraPaths")).toStringList());
234 result.sysroot = map.value(QStringLiteral("sysroot")).toString().toStdString();
235 result.topBuildDir = map.value(QStringLiteral("topBuildDir")).toString().toStdString();
236 result.allowSystemLibraryPaths =
237 map.value(QStringLiteral("allowSystemLibraryPaths"), false).toBool();
238 const auto systemLibraryPaths = map.value(QStringLiteral("systemLibraryPaths")).toStringList();
239 result.systemLibraryPaths.reserve(systemLibraryPaths.size());
240 std::transform(
241 systemLibraryPaths.begin(),
242 systemLibraryPaths.end(),
243 std::back_inserter(result.systemLibraryPaths),
244 [](const QString &str){ return str.toStdString(); });
245 result.disableUninstalled = map.value(QStringLiteral("disableUninstalled"), true).toBool();
246 result.staticMode = map.value(QStringLiteral("staticMode"), false).toBool();
247 result.mergeDependencies = map.value(QStringLiteral("mergeDependencies"), true).toBool();
248 result.globalVariables =
249 variablesFromQVariantMap(map.value(QStringLiteral("globalVariables")).toMap());
250 result.systemVariables = envToVariablesMap(env);
251
252 return result;
253 }
254
255 } // namespace Internal
256 } // namespace qbs
257
initializeJsExtensionPkgConfig(QScriptValue extensionObject)258 void initializeJsExtensionPkgConfig(QScriptValue extensionObject)
259 {
260 using namespace qbs::Internal;
261 QScriptEngine *engine = extensionObject.engine();
262 QScriptValue obj = engine->newQMetaObject(
263 &PkgConfigJs::staticMetaObject, engine->newFunction(&PkgConfigJs::ctor));
264 extensionObject.setProperty(QStringLiteral("PkgConfig"), obj);
265 }
266
267 Q_DECLARE_METATYPE(qbs::Internal::PkgConfigJs *)
268