summaryrefslogtreecommitdiffstats
path: root/src/qtattributionsscanner/scanner.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qtattributionsscanner/scanner.cpp')
-rw-r--r--src/qtattributionsscanner/scanner.cpp474
1 files changed, 353 insertions, 121 deletions
diff --git a/src/qtattributionsscanner/scanner.cpp b/src/qtattributionsscanner/scanner.cpp
index 06abc2310..879ce7c80 100644
--- a/src/qtattributionsscanner/scanner.cpp
+++ b/src/qtattributionsscanner/scanner.cpp
@@ -1,35 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "scanner.h"
#include "logging.h"
#include <QtCore/qdir.h>
+#include <QtCore/qhash.h>
#include <QtCore/qjsonarray.h>
#include <QtCore/qjsondocument.h>
#include <QtCore/qjsonobject.h>
@@ -37,7 +13,8 @@
#include <QtCore/qvariant.h>
#include <iostream>
-#include <optional>
+
+using namespace Qt::Literals::StringLiterals;
namespace Scanner {
@@ -47,45 +24,91 @@ static void missingPropertyWarning(const QString &filePath, const QString &prope
QDir::toNativeSeparators(filePath), property)) << std::endl;
}
-static void validatePackage(Package &p, const QString &filePath, LogLevel logLevel)
+static bool validatePackage(Package &p, const QString &filePath, Checks checks, LogLevel logLevel)
{
+ bool validPackage = true;
+
if (p.qtParts.isEmpty())
- p.qtParts << QStringLiteral("libs");
+ p.qtParts << u"libs"_s;
- if (logLevel != SilentLog) {
- if (p.name.isEmpty()) {
- if (p.id.startsWith(QLatin1String("chromium-"))) // Ignore invalid README.chromium files
- return;
+ if (p.name.isEmpty()) {
+ if (p.id.startsWith("chromium-"_L1)) // Ignore invalid README.chromium files
+ return false;
- missingPropertyWarning(filePath, QStringLiteral("Name"));
- }
+ if (logLevel != SilentLog)
+ missingPropertyWarning(filePath, u"Name"_s);
+ validPackage = false;
+ }
- if (p.id.isEmpty())
- missingPropertyWarning(filePath, QStringLiteral("Id"));
- if (p.license.isEmpty())
- missingPropertyWarning(filePath, QStringLiteral("License"));
+ if (p.id.isEmpty()) {
+ if (logLevel != SilentLog)
+ missingPropertyWarning(filePath, u"Id"_s);
+ validPackage = false;
+ }
+ if (p.license.isEmpty()) {
+ if (logLevel != SilentLog)
+ missingPropertyWarning(filePath, u"License"_s);
+ validPackage = false;
+ }
- if (!p.copyright.isEmpty() && !p.copyrightFile.isEmpty()) {
+ if (!p.copyright.isEmpty() && !p.copyrightFile.isEmpty()) {
+ if (logLevel != SilentLog) {
std::cerr << qPrintable(tr("File %1: Properties 'Copyright' and 'CopyrightFile' are "
"mutually exclusive.")
.arg(QDir::toNativeSeparators(filePath)))
<< std::endl;
}
+ validPackage = false;
+ }
- for (const QString &part : qAsConst(p.qtParts)) {
- if (part != QLatin1String("examples")
- && part != QLatin1String("tests")
- && part != QLatin1String("tools")
- && part != QLatin1String("libs")
- && logLevel != SilentLog) {
+ if (p.securityCritical && p.downloadLocation.isEmpty()) {
+ if (logLevel != SilentLog)
+ missingPropertyWarning(filePath, u"DownloadLocation"_s);
+ validPackage = false;
+ }
+
+ for (const QString &part : std::as_const(p.qtParts)) {
+ if (part != "examples"_L1 && part != "tests"_L1
+ && part != "tools"_L1 && part != "libs"_L1) {
+
+ if (logLevel != SilentLog) {
std::cerr << qPrintable(tr("File %1: Property 'QtPart' contains unknown element "
"'%2'. Valid entries are 'examples', 'tests', 'tools' "
"and 'libs'.").arg(
QDir::toNativeSeparators(filePath), part))
<< std::endl;
}
+ validPackage = false;
}
}
+
+ if (!(checks & Check::Paths))
+ return validPackage;
+
+ const QDir dir = p.path;
+ if (!dir.exists()) {
+ std::cerr << qPrintable(
+ tr("File %1: Directory '%2' does not exist.")
+ .arg(QDir::toNativeSeparators(filePath), QDir::toNativeSeparators(p.path)))
+ << std::endl;
+ validPackage = false;
+ } else {
+ for (const QString &file : std::as_const(p.files)) {
+ if (!dir.exists(file)) {
+ if (logLevel != SilentLog) {
+ std::cerr << qPrintable(
+ tr("File %1: Path '%2' does not exist in directory '%3'.")
+ .arg(QDir::toNativeSeparators(filePath),
+ QDir::toNativeSeparators(file),
+ QDir::toNativeSeparators(p.path)))
+ << std::endl;
+ }
+ validPackage = false;
+ }
+ }
+ }
+
+ return validPackage;
}
static std::optional<QStringList> toStringList(const QJsonValue &value)
@@ -93,7 +116,7 @@ static std::optional<QStringList> toStringList(const QJsonValue &value)
if (!value.isArray())
return std::nullopt;
QStringList result;
- for (auto iter : value.toArray()) {
+ for (const auto &iter : value.toArray()) {
if (iter.type() != QJsonValue::String)
return std::nullopt;
result.push_back(iter.toString());
@@ -101,80 +124,257 @@ static std::optional<QStringList> toStringList(const QJsonValue &value)
return result;
}
+static std::optional<QString> arrayToMultiLineString(const QJsonValue &value)
+{
+ if (!value.isArray())
+ return std::nullopt;
+ QString result;
+ for (const auto &iter : value.toArray()) {
+ if (iter.type() != QJsonValue::String)
+ return std::nullopt;
+ result.append(iter.toString());
+ result.append(QLatin1StringView("\n"));
+ }
+ return result;
+}
+
+// Extracts SPDX license ids from a SPDX license expression.
+// For "(BSD-3-Clause AND BeerWare)" this function returns { "BSD-3-Clause", "BeerWare" }.
+static QStringList extractLicenseIdsFromSPDXExpression(QString expression)
+{
+ const QStringList spdxOperators = {
+ u"AND"_s,
+ u"OR"_s,
+ u"WITH"_s
+ };
+
+ // Replace parentheses with spaces. We're not interested in grouping.
+ const QRegularExpression parensRegex(u"[()]"_s);
+ expression.replace(parensRegex, u" "_s);
+
+ // Split the string at space boundaries to extract tokens.
+ QStringList result;
+ for (const QString &token : expression.split(QLatin1Char(' '), Qt::SkipEmptyParts)) {
+ if (spdxOperators.contains(token))
+ continue;
+
+ // Remove the unary + operator, if present.
+ if (token.endsWith(QLatin1Char('+')))
+ result.append(token.mid(0, token.size() - 1));
+ else
+ result.append(token);
+ }
+ return result;
+}
+
+// Starting at packageDir, look for a LICENSES subdirectory in the directory hierarchy upwards.
+// Return a default-constructed QString if the directory was not found.
+static QString locateLicensesDir(const QString &packageDir)
+{
+ static const QString licensesSubDir = u"LICENSES"_s;
+ QDir dir(packageDir);
+ while (true) {
+ if (!dir.exists())
+ break;
+ if (dir.cd(licensesSubDir))
+ return dir.path();
+ if (dir.isRoot() || !dir.cdUp())
+ break;
+ }
+ return {};
+}
+
+// Locates the license files that belong to the licenses mentioned in LicenseId and stores them in
+// the specified package object.
+static bool autoDetectLicenseFiles(Package &p)
+{
+ const QString licensesDirPath = locateLicensesDir(p.path);
+ const QStringList licenseIds = extractLicenseIdsFromSPDXExpression(p.licenseId);
+ if (!licenseIds.isEmpty() && licensesDirPath.isEmpty()) {
+ std::cerr << qPrintable(tr("LICENSES directory could not be located.")) << std::endl;
+ return false;
+ }
+
+ bool success = true;
+ QDir licensesDir(licensesDirPath);
+ for (const QString &id : licenseIds) {
+ QString fileName = id + u".txt";
+ if (licensesDir.exists(fileName)) {
+ p.licenseFiles.append(licensesDir.filePath(fileName));
+ } else {
+ std::cerr << qPrintable(tr("Expected license file not found: %1").arg(
+ QDir::toNativeSeparators(licensesDir.filePath(fileName))))
+ << std::endl;
+ success = false;
+ }
+ }
+
+ return success;
+}
+
// Transforms a JSON object into a Package object
-static Package readPackage(const QJsonObject &object, const QString &filePath, LogLevel logLevel)
+static std::optional<Package> readPackage(const QJsonObject &object, const QString &filePath,
+ Checks checks, LogLevel logLevel)
{
Package p;
+ bool validPackage = true;
const QString directory = QFileInfo(filePath).absolutePath();
p.path = directory;
for (auto iter = object.constBegin(); iter != object.constEnd(); ++iter) {
const QString key = iter.key();
- if (!iter.value().isString() && key != QLatin1String("QtParts")
- && key != QLatin1String("LicenseFiles")) {
+ if (!iter.value().isString() && key != "QtParts"_L1 && key != "SecurityCritical"_L1
+ && key != "Files"_L1 && key != "LicenseFiles"_L1 && key != "Comment"_L1
+ && key != "Copyright"_L1) {
if (logLevel != SilentLog)
std::cerr << qPrintable(tr("File %1: Expected JSON string as value of %2.").arg(
QDir::toNativeSeparators(filePath), key)) << std::endl;
+ validPackage = false;
continue;
}
const QString value = iter.value().toString();
- if (key == QLatin1String("Name")) {
+ if (key == "Name"_L1) {
p.name = value;
- } else if (key == QLatin1String("Path")) {
+ } else if (key == "Path"_L1) {
p.path = QDir(directory).absoluteFilePath(value);
- } else if (key == QLatin1String("Files")) {
- p.files = value.simplified().split(QLatin1Char(' '), Qt::SkipEmptyParts);
- } else if (key == QLatin1String("Id")) {
+ } else if (key == "Files"_L1) {
+ QJsonValueConstRef jsonValue = iter.value();
+ if (jsonValue.isArray()) {
+ auto maybeStringList = toStringList(jsonValue);
+ if (maybeStringList)
+ p.files = maybeStringList.value();
+ } else if (jsonValue.isString()) {
+ // Legacy format: multiple values separated by space in one string.
+ p.files = value.simplified().split(QLatin1Char(' '), Qt::SkipEmptyParts);
+ } else {
+ if (logLevel != SilentLog) {
+ std::cerr << qPrintable(tr("File %1: Expected JSON array of strings as value "
+ "of Files."));
+ validPackage = false;
+ continue;
+ }
+ }
+ } else if (key == "Comment"_L1) {
+ // Accepted purely to record details of potential interest doing
+ // updates in future. Value is an arbitrary object. Any number of
+ // Comment entries may be present: JSON doesn't require names to be
+ // unique, albeit some linters may kvetch.
+ } else if (key == "Id"_L1) {
p.id = value;
- } else if (key == QLatin1String("Homepage")) {
+ } else if (key == "Homepage"_L1) {
p.homepage = value;
- } else if (key == QLatin1String("Version")) {
+ } else if (key == "Version"_L1) {
p.version = value;
- } else if (key == QLatin1String("DownloadLocation")) {
+ } else if (key == "DownloadLocation"_L1) {
p.downloadLocation = value;
- } else if (key == QLatin1String("License")) {
+ } else if (key == "License"_L1) {
p.license = value;
- } else if (key == QLatin1String("LicenseId")) {
+ } else if (key == "LicenseId"_L1) {
p.licenseId = value;
- } else if (key == QLatin1String("LicenseFile")) {
+ } else if (key == "LicenseFile"_L1) {
p.licenseFiles = QStringList(QDir(directory).absoluteFilePath(value));
- } else if (key == QLatin1String("LicenseFiles")) {
+ } else if (key == "LicenseFiles"_L1) {
auto strings = toStringList(iter.value());
- if (!strings && (logLevel != SilentLog))
- std::cerr << qPrintable(tr("File %1: Expected JSON array of strings in %2.")
- .arg(QDir::toNativeSeparators(filePath), key))
- << std::endl;
+ if (!strings) {
+ if (logLevel != SilentLog)
+ std::cerr << qPrintable(tr("File %1: Expected JSON array of strings in %2.")
+ .arg(QDir::toNativeSeparators(filePath), key))
+ << std::endl;
+ validPackage = false;
+ continue;
+ }
const QDir dir(directory);
- for (auto iter : strings.value())
+ for (const auto &iter : std::as_const(strings.value()))
p.licenseFiles.push_back(dir.absoluteFilePath(iter));
- } else if (key == QLatin1String("Copyright")) {
- p.copyright = value;
- } else if (key == QLatin1String("CopyrightFile")) {
+ } else if (key == "Copyright"_L1) {
+ QJsonValueConstRef jsonValue = iter.value();
+ if (jsonValue.isArray()) {
+ // Array joined with new lines
+ auto maybeString = arrayToMultiLineString(jsonValue);
+ if (maybeString)
+ p.copyright = maybeString.value();
+ } else if (jsonValue.isString()) {
+ // Legacy format: multiple values separated by space in one string.
+ p.copyright = value;
+ } else {
+ if (logLevel != SilentLog) {
+ std::cerr << qPrintable(tr("File %1: Expected JSON array of string or"
+ "string as value of %2.").arg(
+ QDir::toNativeSeparators(filePath), key)) << std::endl;
+ validPackage = false;
+ continue;
+ }
+ }
+ } else if (key == "CopyrightFile"_L1) {
p.copyrightFile = QDir(directory).absoluteFilePath(value);
- } else if (key == QLatin1String("PackageComment")) {
+ } else if (key == "PackageComment"_L1) {
p.packageComment = value;
- } else if (key == QLatin1String("QDocModule")) {
+ } else if (key == "QDocModule"_L1) {
p.qdocModule = value;
- } else if (key == QLatin1String("Description")) {
+ } else if (key == "Description"_L1) {
p.description = value;
- } else if (key == QLatin1String("QtUsage")) {
+ } else if (key == "QtUsage"_L1) {
p.qtUsage = value;
- } else if (key == QLatin1String("QtParts")) {
- auto parts = toStringList(iter.value());
- if (!parts && (logLevel != SilentLog))
- std::cerr << qPrintable(tr("File %1: Expected JSON array of strings in %2.")
+ } else if (key == "SecurityCritical"_L1) {
+ if (!iter.value().isBool()) {
+ std::cerr << qPrintable(tr("File %1: Expected JSON boolean in %2.")
.arg(QDir::toNativeSeparators(filePath), key))
<< std::endl;
+ validPackage = false;
+ continue;
+ }
+ p.securityCritical = iter.value().toBool();
+ } else if (key == "QtParts"_L1) {
+ auto parts = toStringList(iter.value());
+ if (!parts) {
+ if (logLevel != SilentLog) {
+ std::cerr << qPrintable(tr("File %1: Expected JSON array of strings in %2.")
+ .arg(QDir::toNativeSeparators(filePath), key))
+ << std::endl;
+ }
+ validPackage = false;
+ continue;
+ }
p.qtParts = parts.value();
} else {
- if (logLevel != SilentLog)
+ if (logLevel != SilentLog) {
std::cerr << qPrintable(tr("File %1: Unknown key %2.").arg(
QDir::toNativeSeparators(filePath), key)) << std::endl;
+ }
+ validPackage = false;
+ }
+ }
+
+ if (!p.copyrightFile.isEmpty()) {
+ QFile file(p.copyrightFile);
+ if (!file.open(QIODevice::ReadOnly)) {
+ std::cerr << qPrintable(tr("File %1: Cannot open 'CopyrightFile' %2.\n")
+ .arg(QDir::toNativeSeparators(filePath),
+ QDir::toNativeSeparators(p.copyrightFile)));
+ validPackage = false;
+ }
+ p.copyrightFileContents = QString::fromUtf8(file.readAll());
+ }
+
+ if (p.licenseFiles.isEmpty() && !autoDetectLicenseFiles(p))
+ return std::nullopt;
+
+ for (const QString &licenseFile : std::as_const(p.licenseFiles)) {
+ QFile file(licenseFile);
+ if (!file.open(QIODevice::ReadOnly)) {
+ if (logLevel != SilentLog) {
+ std::cerr << qPrintable(tr("File %1: Cannot open 'LicenseFile' %2.\n")
+ .arg(QDir::toNativeSeparators(filePath),
+ QDir::toNativeSeparators(licenseFile)));
+ }
+ validPackage = false;
}
+ p.licenseFilesContents << QString::fromUtf8(file.readAll()).trimmed();
}
- validatePackage(p, filePath, logLevel);
+ if (!validatePackage(p, filePath, checks, logLevel) || !validPackage)
+ return std::nullopt;
return p;
}
@@ -190,9 +390,9 @@ static Package parseChromiumFile(QFile &file, const QString &filePath, LogLevel
QTextStream in(&file);
while (!in.atEnd()) {
QString line = in.readLine().trimmed();
- QStringList parts = line.split(QStringLiteral(":"));
+ QStringList parts = line.split(u":"_s);
- if (parts.count() < 2)
+ if (parts.size() < 2)
continue;
QString key = parts.at(0);
@@ -201,14 +401,14 @@ static Package parseChromiumFile(QFile &file, const QString &filePath, LogLevel
fields[key] = value;
- if (line == QLatin1String("Description:")) { // special field : should handle multi-lines values
+ if (line == "Description:"_L1) { // special field : should handle multi-lines values
while (!in.atEnd()) {
QString line = in.readLine().trimmed();
- if (line.startsWith(QLatin1String("Local Modifications:"))) // Don't include this part
+ if (line.startsWith("Local Modifications:"_L1)) // Don't include this part
break;
- fields[key] += line + QStringLiteral("\n");
+ fields[key] += line + u"\n"_s;
}
break;
@@ -218,30 +418,30 @@ static Package parseChromiumFile(QFile &file, const QString &filePath, LogLevel
// Construct the Package object
Package p;
- QString shortName = fields.contains(QLatin1String("Short Name"))
- ? fields[QLatin1String("Short Name")]
- : fields[QLatin1String("Name")];
- QString version = fields[QStringLiteral("Version")];
+ QString shortName = fields.contains("Short Name"_L1)
+ ? fields["Short Name"_L1]
+ : fields["Name"_L1];
+ QString version = fields[u"Version"_s];
- p.id = QStringLiteral("chromium-") + shortName.toLower().replace(QChar::Space, QStringLiteral("-"));
- p.name = fields[QStringLiteral("Name")];
+ p.id = u"chromium-"_s + shortName.toLower().replace(QChar::Space, u"-"_s);
+ p.name = fields[u"Name"_s];
if (version != QLatin1Char('0')) // "0" : not applicable
p.version = version;
- p.license = fields[QStringLiteral("License")];
- p.homepage = fields[QStringLiteral("URL")];
- p.qdocModule = QStringLiteral("qtwebengine");
- p.qtUsage = QStringLiteral("Used in Qt WebEngine");
- p.description = fields[QStringLiteral("Description")].trimmed();
+ p.license = fields[u"License"_s];
+ p.homepage = fields[u"URL"_s];
+ p.qdocModule = u"qtwebengine"_s;
+ p.qtUsage = u"Used in Qt WebEngine"_s;
+ p.description = fields[u"Description"_s].trimmed();
p.path = directory;
- QString licenseFile = fields[QStringLiteral("License File")];
- if (licenseFile != QString() && licenseFile != QLatin1String("NOT_SHIPPED")) {
+ QString licenseFile = fields[u"License File"_s];
+ if (licenseFile != QString() && licenseFile != "NOT_SHIPPED"_L1) {
p.licenseFiles = QStringList(QDir(directory).absoluteFilePath(licenseFile));
} else {
// Look for a LICENSE or COPYING file as a fallback
QDir dir = directory;
- dir.setNameFilters({ QStringLiteral("LICENSE"), QStringLiteral("COPYING") });
+ dir.setNameFilters({ u"LICENSE"_s, u"COPYING"_s });
dir.setFilter(QDir::Files | QDir::NoDotAndDotDot);
const QFileInfoList entries = dir.entryInfoList();
@@ -249,14 +449,16 @@ static Package parseChromiumFile(QFile &file, const QString &filePath, LogLevel
p.licenseFiles = QStringList(entries.at(0).absoluteFilePath());
}
- validatePackage(p, filePath, logLevel);
+ // let's ignore warnings regarding Chromium files for now
+ Q_UNUSED(validatePackage(p, filePath, {}, logLevel));
return p;
}
-QList<Package> readFile(const QString &filePath, LogLevel logLevel)
+std::optional<QList<Package>> readFile(const QString &filePath, Checks checks, LogLevel logLevel)
{
QList<Package> packages;
+ bool errorsFound = false;
if (logLevel == VerboseLog) {
std::cerr << qPrintable(tr("Reading file %1...").arg(
@@ -267,10 +469,10 @@ QList<Package> readFile(const QString &filePath, LogLevel logLevel)
if (logLevel != SilentLog)
std::cerr << qPrintable(tr("Could not open file %1.").arg(
QDir::toNativeSeparators(file.fileName()))) << std::endl;
- return QList<Package>();
+ return std::nullopt;
}
- if (filePath.endsWith(QLatin1String(".json"))) {
+ if (filePath.endsWith(".json"_L1)) {
QJsonParseError jsonParseError;
const QJsonDocument document = QJsonDocument::fromJson(file.readAll(), &jsonParseError);
if (document.isNull()) {
@@ -279,58 +481,77 @@ QList<Package> readFile(const QString &filePath, LogLevel logLevel)
QDir::toNativeSeparators(file.fileName()),
jsonParseError.errorString()))
<< std::endl;
- return QList<Package>();
+ return std::nullopt;
}
if (document.isObject()) {
- packages << readPackage(document.object(), file.fileName(), logLevel);
+ std::optional<Package> p =
+ readPackage(document.object(), file.fileName(), checks, logLevel);
+ if (p) {
+ packages << *p;
+ } else {
+ errorsFound = true;
+ }
} else if (document.isArray()) {
QJsonArray array = document.array();
for (int i = 0, size = array.size(); i < size; ++i) {
QJsonValue value = array.at(i);
if (value.isObject()) {
- packages << readPackage(value.toObject(), file.fileName(), logLevel);
+ std::optional<Package> p =
+ readPackage(value.toObject(), file.fileName(), checks, logLevel);
+ if (p) {
+ packages << *p;
+ } else {
+ errorsFound = true;
+ }
} else {
- if (logLevel != SilentLog)
+ if (logLevel != SilentLog) {
std::cerr << qPrintable(tr("File %1: Expecting JSON object in array.")
.arg(QDir::toNativeSeparators(file.fileName())))
<< std::endl;
+ }
+ errorsFound = true;
}
}
} else {
- if (logLevel != SilentLog)
+ if (logLevel != SilentLog) {
std::cerr << qPrintable(tr("File %1: Expecting JSON object in array.").arg(
QDir::toNativeSeparators(file.fileName()))) << std::endl;
+ }
+ errorsFound = true;
}
- } else if (filePath.endsWith(QLatin1String(".chromium"))) {
+ } else if (filePath.endsWith(".chromium"_L1)) {
Package chromiumPackage = parseChromiumFile(file, filePath, logLevel);
if (!chromiumPackage.name.isEmpty()) // Skip invalid README.chromium files
packages << chromiumPackage;
} else {
- if (logLevel != SilentLog)
+ if (logLevel != SilentLog) {
std::cerr << qPrintable(tr("File %1: Unsupported file type.")
.arg(QDir::toNativeSeparators(file.fileName())))
<< std::endl;
+ }
+ errorsFound = true;
}
+ if (errorsFound)
+ return std::nullopt;
return packages;
}
-QList<Package> scanDirectory(const QString &directory, InputFormats inputFormats, LogLevel logLevel)
+std::optional<QList<Package>> scanDirectory(const QString &directory, InputFormats inputFormats,
+ Checks checks, LogLevel logLevel)
{
QDir dir(directory);
QList<Package> packages;
+ bool errorsFound = false;
QStringList nameFilters = QStringList();
if (inputFormats & InputFormat::QtAttributions)
- nameFilters << QStringLiteral("qt_attribution.json");
+ nameFilters << u"qt_attribution.json"_s;
if (inputFormats & InputFormat::ChromiumAttributions)
- nameFilters << QStringLiteral("README.chromium");
- if (qEnvironmentVariableIsSet("QT_ATTRIBUTIONSSCANNER_TEST")) {
- nameFilters
- << QStringLiteral("qt_attribution_test.json")
- << QStringLiteral("README_test.chromium");
- }
+ nameFilters << u"README.chromium"_s;
+ if (qEnvironmentVariableIsSet("QT_ATTRIBUTIONSSCANNER_TEST"))
+ nameFilters << u"qt_attribution_test.json"_s << u"README_test.chromium"_s;
dir.setNameFilters(nameFilters);
dir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Files);
@@ -338,12 +559,23 @@ QList<Package> scanDirectory(const QString &directory, InputFormats inputFormats
const QFileInfoList entries = dir.entryInfoList();
for (const QFileInfo &info : entries) {
if (info.isDir()) {
- packages += scanDirectory(info.filePath(), inputFormats, logLevel);
+ std::optional<QList<Package>> ps =
+ scanDirectory(info.filePath(), inputFormats, checks, logLevel);
+ if (!ps)
+ errorsFound = true;
+ else
+ packages += *ps;
} else {
- packages += readFile(info.filePath(), logLevel);
+ std::optional p = readFile(info.filePath(), checks, logLevel);
+ if (!p)
+ errorsFound = true;
+ else
+ packages += *p;
}
}
+ if (errorsFound)
+ return std::nullopt;
return packages;
}