aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qmllint/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/qmllint/main.cpp')
-rw-r--r--tools/qmllint/main.cpp230
1 files changed, 163 insertions, 67 deletions
diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp
index 5c35c19928..80733382ef 100644
--- a/tools/qmllint/main.cpp
+++ b/tools/qmllint/main.cpp
@@ -1,11 +1,14 @@
// Copyright (C) 2016 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sergio Martins <sergio.martins@kdab.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "../shared/qqmltoolingsettings.h"
+#include <QtQmlToolingSettings/private/qqmltoolingsettings_p.h>
+#include <QtQmlToolingSettings/private/qqmltoolingutils_p.h>
-#include <QtQmlCompiler/private/qqmljsresourcefilemapper_p.h>
#include <QtQmlCompiler/private/qqmljscompiler_p.h>
#include <QtQmlCompiler/private/qqmljslinter_p.h>
+#include <QtQmlCompiler/private/qqmljsloggingutils_p.h>
+#include <QtQmlCompiler/private/qqmljsresourcefilemapper_p.h>
+#include <QtQmlCompiler/private/qqmljsutils_p.h>
#include <QtCore/qdebug.h>
#include <QtCore/qfile.h>
@@ -29,10 +32,39 @@ using namespace Qt::StringLiterals;
constexpr int JSON_LOGGING_FORMAT_REVISION = 3;
+bool argumentsFromCommandLineAndFile(QStringList& allArguments, const QStringList &arguments)
+{
+ allArguments.reserve(arguments.size());
+ for (const QString &argument : arguments) {
+ // "@file" doesn't start with a '-' so we can't use QCommandLineParser for it
+ if (argument.startsWith(u'@')) {
+ QString optionsFile = argument;
+ optionsFile.remove(0, 1);
+ if (optionsFile.isEmpty()) {
+ qWarning().nospace() << "The @ option requires an input file";
+ return false;
+ }
+ QFile f(optionsFile);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning().nospace() << "Cannot open options file specified with @";
+ return false;
+ }
+ while (!f.atEnd()) {
+ QString line = QString::fromLocal8Bit(f.readLine().trimmed());
+ if (!line.isEmpty())
+ allArguments << line;
+ }
+ } else {
+ allArguments << argument;
+ }
+ }
+ return true;
+}
+
int main(int argv, char *argc[])
{
QHashSeed::setDeterministicGlobalSeed();
- QList<QQmlJSLogger::Category> categories;
+ QList<QQmlJS::LoggerCategory> categories;
QCoreApplication app(argv, argc);
QCoreApplication::setApplicationName("qmllint");
@@ -92,6 +124,11 @@ All warnings can be set to three levels:
const QString qmlImportPathsSetting = QLatin1String("AdditionalQmlImportPaths");
settings.addOption(qmlImportPathsSetting);
+ QCommandLineOption environmentOption(
+ QStringList() << "E",
+ QLatin1String("Use the QML_IMPORT_PATH environment variable to look for QML Modules"));
+ parser.addOption(environmentOption);
+
QCommandLineOption qmlImportNoDefault(
QStringList() << "bare",
QLatin1String("Do not include default import directories or the current directory. "
@@ -150,20 +187,36 @@ All warnings can be set to three levels:
QLatin1String("directory"));
parser.addOption(pluginPathsOption);
- auto addCategory = [&](const QQmlJSLogger::Category &category) {
+ auto levelToString = [](const QQmlJS::LoggerCategory &category) -> QString {
+ Q_ASSERT(category.isIgnored() || category.level() != QtCriticalMsg);
+ if (category.isIgnored())
+ return QStringLiteral("disable");
+
+ switch (category.level()) {
+ case QtInfoMsg:
+ return QStringLiteral("info");
+ case QtWarningMsg:
+ return QStringLiteral("warning");
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ };
+
+ auto addCategory = [&](const QQmlJS::LoggerCategory &category) {
categories.push_back(category);
- if (category.isDefault)
+ if (category.isDefault())
return;
QCommandLineOption option(
category.id().name().toString(),
- category.description
- + QStringLiteral(" (default: %1)").arg(category.levelToString()),
- QStringLiteral("level"), category.levelToString());
- if (category.ignored)
+ category.description()
+ + QStringLiteral(" (default: %1)").arg(levelToString(category)),
+ QStringLiteral("level"), levelToString(category));
+ if (category.isIgnored())
option.setFlags(QCommandLineOption::HiddenFromHelp);
parser.addOption(option);
- settings.addOption(QStringLiteral("Warnings/") + category.settingsName,
- category.levelToString());
+ settings.addOption(QStringLiteral("Warnings/") + category.settingsName(),
+ levelToString(category));
};
for (const auto &category : QQmlJSLogger::defaultCategories()) {
@@ -172,11 +225,16 @@ All warnings can be set to three levels:
parser.addPositionalArgument(QLatin1String("files"),
QLatin1String("list of qml or js files to verify"));
- if (!parser.parse(app.arguments())) {
- if (parser.unknownOptionNames().isEmpty()) {
- qWarning().noquote() << parser.errorText();
- return 1;
- }
+
+ QStringList arguments;
+ if (!argumentsFromCommandLineAndFile(arguments, app.arguments())) {
+ // argumentsFromCommandLine already printed any necessary warnings.
+ return 1;
+ }
+
+ if (!parser.parse(arguments)) {
+ qWarning().noquote() << parser.errorText();
+ return 1;
}
// Since we can't use QCommandLineParser::process(), we need to handle version and help manually
@@ -192,21 +250,30 @@ All warnings can be set to three levels:
auto updateLogLevels = [&]() {
for (auto &category : categories) {
- if (category.isDefault)
+ if (category.isDefault())
continue;
const QString &key = category.id().name().toString();
- const QString &settingsName = QStringLiteral("Warnings/") + category.settingsName;
+ const QString &settingsName = QStringLiteral("Warnings/") + category.settingsName();
if (parser.isSet(key) || settings.isSet(settingsName)) {
const QString value = parser.isSet(key) ? parser.value(key)
: settings.value(settingsName).toString();
// Do not try to set the levels if it's due to a default config option.
// This way we can tell which options have actually been overwritten by the user.
- if (category.levelToString() == value && !parser.isSet(key))
+ if (levelToString(category) == value && !parser.isSet(key))
continue;
- if (!category.setLevel(value)) {
+ if (value == "disable"_L1) {
+ category.setLevel(QtCriticalMsg);
+ category.setIgnored(true);
+ } else if (value == "info"_L1) {
+ category.setLevel(QtInfoMsg);
+ category.setIgnored(false);
+ } else if (value == "warning"_L1) {
+ category.setLevel(QtWarningMsg);
+ category.setIgnored(false);
+ } else {
qWarning() << "Invalid logging level" << value << "provided for"
<< category.id().name().toString()
<< "(allowed are: disable, info, warning)";
@@ -235,8 +302,8 @@ All warnings can be set to three levels:
QStringList defaultQmldirFiles;
if (parser.isSet(qmldirFilesOption)) {
- defaultQmldirFiles = parser.values(qmldirFilesOption);
- } else {
+ defaultQmldirFiles = QQmlJSUtils::cleanPaths(parser.values(qmldirFilesOption));
+ } else if (!parser.isSet(qmlImportNoDefault)){
// If nothing given explicitly, use the qmldir file from the current directory.
QFileInfo qmldirFile(QStringLiteral("qmldir"));
if (qmldirFile.isFile()) {
@@ -267,7 +334,7 @@ All warnings can be set to three levels:
QQmlJSLinter linter(qmlImportPaths, pluginPaths, useAbsolutePath);
for (const QQmlJSLinter::Plugin &plugin : linter.plugins()) {
- for (const QQmlJSLogger::Category &category : plugin.categories())
+ for (const QQmlJS::LoggerCategory &category : plugin.categories())
addCategory(category);
}
@@ -300,70 +367,97 @@ All warnings can be set to three levels:
QJsonArray jsonFiles;
for (const QString &filename : positionalArguments) {
- if (!parser.isSet(ignoreSettings)) {
+ if (!parser.isSet(ignoreSettings))
settings.search(filename);
- updateLogLevels();
+ updateLogLevels();
- const QDir fileDir = QFileInfo(filename).absoluteDir();
- auto addAbsolutePaths = [&](QStringList &list, const QStringList &entries) {
- for (const QString &file : entries)
- list << (QFileInfo(file).isAbsolute() ? file : fileDir.filePath(file));
- };
+ const QDir fileDir = QFileInfo(filename).absoluteDir();
+ auto addAbsolutePaths = [&](QStringList &list, const QStringList &entries) {
+ for (const QString &file : entries)
+ list << (QFileInfo(file).isAbsolute() ? file : fileDir.filePath(file));
+ };
- resourceFiles = defaultResourceFiles;
+ resourceFiles = defaultResourceFiles;
- addAbsolutePaths(resourceFiles, settings.value(resourceSetting).toStringList());
+ addAbsolutePaths(resourceFiles, settings.value(resourceSetting).toStringList());
- qmldirFiles = defaultQmldirFiles;
- if (settings.isSet(qmldirFilesSetting)
- && !settings.value(qmldirFilesSetting).toStringList().isEmpty()) {
- qmldirFiles = {};
- addAbsolutePaths(qmldirFiles,
- settings.value(qmldirFilesSetting).toStringList());
- }
+ qmldirFiles = defaultQmldirFiles;
+ if (settings.isSet(qmldirFilesSetting)
+ && !settings.value(qmldirFilesSetting).toStringList().isEmpty()) {
+ qmldirFiles = {};
+ addAbsolutePaths(qmldirFiles, settings.value(qmldirFilesSetting).toStringList());
+ }
- if (parser.isSet(qmlImportNoDefault)
- || (settings.isSet(qmlImportNoDefaultSetting)
- && settings.value(qmlImportNoDefaultSetting).toBool())) {
- qmlImportPaths = {};
+ if (parser.isSet(qmlImportNoDefault)
+ || (settings.isSet(qmlImportNoDefaultSetting)
+ && settings.value(qmlImportNoDefaultSetting).toBool())) {
+ qmlImportPaths = {};
+ } else {
+ qmlImportPaths = defaultImportPaths;
+ }
+
+ if (parser.isSet(qmlImportPathsOption))
+ qmlImportPaths << parser.values(qmlImportPathsOption);
+ if (parser.isSet(environmentOption)) {
+ if (silent) {
+ qmlImportPaths << qEnvironmentVariable("QML_IMPORT_PATH")
+ .split(QDir::separator(), Qt::SkipEmptyParts)
+ << qEnvironmentVariable("QML2_IMPORT_PATH")
+ .split(QDir::separator(), Qt::SkipEmptyParts);
} else {
- qmlImportPaths = defaultImportPaths;
+ if (const QStringList dirsFromEnv =
+ QQmlToolingUtils::getAndWarnForInvalidDirsFromEnv(u"QML_IMPORT_PATH"_s);
+ !dirsFromEnv.isEmpty()) {
+ qInfo().nospace().noquote()
+ << "Using import directories passed from environment variable "
+ "\"QML_IMPORT_PATH\": \""
+ << dirsFromEnv.join(u"\", \""_s) << "\".";
+ qmlImportPaths << dirsFromEnv;
+ }
+ if (const QStringList dirsFromEnv =
+ QQmlToolingUtils::getAndWarnForInvalidDirsFromEnv(
+ u"QML2_IMPORT_PATH"_s);
+ !dirsFromEnv.isEmpty()) {
+ qInfo().nospace().noquote() << "Using import directories passed from the "
+ "deprecated environment variable "
+ "\"QML2_IMPORT_PATH\": \""
+ << dirsFromEnv.join(u"\", \""_s) << "\".";
+ qmlImportPaths << dirsFromEnv;
+ }
}
+ }
- if (parser.isSet(qmlImportPathsOption))
- qmlImportPaths << parser.values(qmlImportPathsOption);
-
- addAbsolutePaths(qmlImportPaths, settings.value(qmlImportPathsSetting).toStringList());
+ addAbsolutePaths(qmlImportPaths, settings.value(qmlImportPathsSetting).toStringList());
- QSet<QString> disabledPlugins;
+ QSet<QString> disabledPlugins;
- if (parser.isSet(pluginsDisable)) {
- for (const QString &plugin : parser.values(pluginsDisable))
- disabledPlugins << plugin.toLower();
- }
+ if (parser.isSet(pluginsDisable)) {
+ for (const QString &plugin : parser.values(pluginsDisable))
+ disabledPlugins << plugin.toLower();
+ }
- if (settings.isSet(pluginsDisableSetting)) {
- for (const QString &plugin : settings.value(pluginsDisableSetting).toStringList())
- disabledPlugins << plugin.toLower();
- }
+ if (settings.isSet(pluginsDisableSetting)) {
+ for (const QString &plugin : settings.value(pluginsDisableSetting).toStringList())
+ disabledPlugins << plugin.toLower();
+ }
- linter.setPluginsEnabled(!disabledPlugins.contains("all"));
+ linter.setPluginsEnabled(!disabledPlugins.contains("all"));
- if (!linter.pluginsEnabled())
- continue;
+ if (!linter.pluginsEnabled())
+ continue;
- auto &plugins = linter.plugins();
+ auto &plugins = linter.plugins();
- for (auto &plugin : plugins)
- plugin.setEnabled(!disabledPlugins.contains(plugin.name().toLower()));
- }
+ for (auto &plugin : plugins)
+ plugin.setEnabled(!disabledPlugins.contains(plugin.name().toLower()));
const bool isFixing = parser.isSet(fixFile);
QQmlJSLinter::LintResult lintResult;
if (parser.isSet(moduleOption)) {
- lintResult = linter.lintModule(filename, silent, useJson ? &jsonFiles : nullptr);
+ lintResult = linter.lintModule(filename, silent, useJson ? &jsonFiles : nullptr,
+ qmlImportPaths, resourceFiles);
} else {
lintResult = linter.lintFile(filename, nullptr, silent || isFixing,
useJson ? &jsonFiles : nullptr, qmlImportPaths,
@@ -451,8 +545,10 @@ All warnings can be set to three levels:
QTextStream(stdout) << QString::fromUtf8(json);
} else {
QFile file(fileName);
- file.open(QFile::WriteOnly);
- file.write(json);
+ if (file.open(QFile::WriteOnly))
+ file.write(json);
+ else
+ success = false;
}
}