1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28
29 #include "qmlutils.h"
30 #include "utils.h"
31
32 #include <QtCore/QDir>
33 #include <QtCore/QFileInfo>
34 #include <QtCore/QCoreApplication>
35 #include <QtCore/QJsonDocument>
36 #include <QtCore/QJsonObject>
37 #include <QtCore/QJsonArray>
38 #include <QtCore/QJsonParseError>
39
40 QT_BEGIN_NAMESPACE
41
operator ==(const QmlImportScanResult::Module & m1,const QmlImportScanResult::Module & m2)42 bool operator==(const QmlImportScanResult::Module &m1, const QmlImportScanResult::Module &m2)
43 {
44 return m1.className.isEmpty() ? m1.name == m2.name : m1.className == m2.className;
45 }
46
47 // Return install path (cp -r semantics)
installPath(const QString & root) const48 QString QmlImportScanResult::Module::installPath(const QString &root) const
49 {
50 QString result = root;
51 const int lastSlashPos = relativePath.lastIndexOf(QLatin1Char('/'));
52 if (lastSlashPos != -1) {
53 result += QLatin1Char('/');
54 result += relativePath.leftRef(lastSlashPos);
55 }
56 return result;
57 }
58
qmlDirectoryRecursion(Platform platform,const QString & path)59 static QString qmlDirectoryRecursion(Platform platform, const QString &path)
60 {
61 QDir dir(path);
62 if (!dir.entryList(QStringList(QStringLiteral("*.qml")), QDir::Files, QDir::NoSort).isEmpty())
63 return dir.path();
64 const QFileInfoList &subDirs = dir.entryInfoList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot, QDir::NoSort);
65 for (const QFileInfo &subDirFi : subDirs) {
66 if (!isBuildDirectory(platform, subDirFi.fileName())) {
67 const QString subPath = qmlDirectoryRecursion(platform, subDirFi.absoluteFilePath());
68 if (!subPath.isEmpty())
69 return subPath;
70 }
71 }
72 return QString();
73 }
74
75 // Find a directory containing QML files in the project
findQmlDirectory(Platform platform,const QString & startDirectoryName)76 QString findQmlDirectory(Platform platform, const QString &startDirectoryName)
77 {
78 QDir startDirectory(startDirectoryName);
79 if (isBuildDirectory(platform, startDirectory.dirName()))
80 startDirectory.cdUp();
81 return qmlDirectoryRecursion(platform, startDirectory.path());
82 }
83
findFileRecursion(const QDir & directory,Platform platform,DebugMatchMode debugMatchMode,QStringList * matches)84 static void findFileRecursion(const QDir &directory, Platform platform,
85 DebugMatchMode debugMatchMode, QStringList *matches)
86 {
87 const QStringList &dlls = findSharedLibraries(directory, platform, debugMatchMode);
88 for (const QString &dll : dlls)
89 matches->append(directory.filePath(dll));
90 const QFileInfoList &subDirs = directory.entryInfoList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
91 for (const QFileInfo &subDirFi : subDirs) {
92 QDir subDirectory(subDirFi.absoluteFilePath());
93 if (subDirectory.isReadable())
94 findFileRecursion(subDirectory, platform, debugMatchMode, matches);
95 }
96 }
97
runQmlImportScanner(const QString & directory,const QStringList & qmlImportPaths,bool usesWidgets,int platform,DebugMatchMode debugMatchMode,QString * errorMessage)98 QmlImportScanResult runQmlImportScanner(const QString &directory, const QStringList &qmlImportPaths,
99 bool usesWidgets, int platform, DebugMatchMode debugMatchMode,
100 QString *errorMessage)
101 {
102 Q_UNUSED(usesWidgets);
103 QmlImportScanResult result;
104 QStringList arguments;
105 for (const QString &importPath : qmlImportPaths)
106 arguments << QStringLiteral("-importPath") << importPath;
107 arguments << QStringLiteral("-rootPath") << directory;
108 unsigned long exitCode;
109 QByteArray stdOut;
110 QByteArray stdErr;
111 const QString binary = QStringLiteral("qmlimportscanner");
112 if (!runProcess(binary, arguments, QDir::currentPath(), &exitCode, &stdOut, &stdErr, errorMessage))
113 return result;
114 if (exitCode) {
115 *errorMessage = binary + QStringLiteral(" returned ") + QString::number(exitCode)
116 + QStringLiteral(": ") + QString::fromLocal8Bit(stdErr);
117 return result;
118 }
119 QJsonParseError jsonParseError{};
120 const QJsonDocument data = QJsonDocument::fromJson(stdOut, &jsonParseError);
121 if (data.isNull() ) {
122 *errorMessage = binary + QStringLiteral(" returned invalid JSON output: ")
123 + jsonParseError.errorString() + QStringLiteral(" :\"")
124 + QString::fromLocal8Bit(stdOut) + QLatin1Char('"');
125 return result;
126 }
127 const QJsonArray array = data.array();
128 const int childCount = array.count();
129 for (int c = 0; c < childCount; ++c) {
130 const QJsonObject object = array.at(c).toObject();
131 if (object.value(QStringLiteral("type")).toString() == QLatin1String("module")) {
132 const QString path = object.value(QStringLiteral("path")).toString();
133 if (!path.isEmpty()) {
134 QmlImportScanResult::Module module;
135 module.name = object.value(QStringLiteral("name")).toString();
136 module.className = object.value(QStringLiteral("classname")).toString();
137 module.sourcePath = path;
138 module.relativePath = object.value(QStringLiteral("relativePath")).toString();
139 result.modules.append(module);
140 findFileRecursion(QDir(path), Platform(platform), debugMatchMode, &result.plugins);
141 }
142 }
143 }
144 result.ok = true;
145 return result;
146 }
147
append(const QmlImportScanResult & other)148 void QmlImportScanResult::append(const QmlImportScanResult &other)
149 {
150 for (const QmlImportScanResult::Module &module : other.modules) {
151 if (std::find(modules.cbegin(), modules.cend(), module) == modules.cend())
152 modules.append(module);
153 }
154 for (const QString &plugin : other.plugins) {
155 if (!plugins.contains(plugin))
156 plugins.append(plugin);
157 }
158 }
159
160 QT_END_NAMESPACE
161