Revision control

Copy as Markdown

// 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 "jsongenerator.h"
#include "logging.h"
#include "packagefilter.h"
#include "qdocgenerator.h"
#include "scanner.h"
#include <QtCore/qcommandlineparser.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdir.h>
#include <QtCore/qfile.h>
#include <iostream>
using namespace Qt::Literals::StringLiterals;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
a.setApplicationName(u"Qt Attributions Scanner"_s);
a.setApplicationVersion(u"1.4"_s);
QCommandLineParser parser;
parser.setApplicationDescription(tr("Processes attribution files in Qt sources."));
parser.addPositionalArgument(u"paths"_s,
tr("Paths to qt_attribution.json/README.chromium files, "
"or directories to be scannned recursively."));
parser.addHelpOption();
parser.addVersionOption();
QCommandLineOption generatorOption(u"output-format"_s,
tr("Output format (\"qdoc\", \"json\")."),
u"generator"_s, u"qdoc"_s);
QCommandLineOption inputFormatOption(u"input-files"_s,
tr("Input files (\"qt_attributions\" scans for "
"qt_attribution.json, \"chromium_attributions\" for "
"README.Chromium, \"all\" for both)."),
u"input_format"_s,
u"qt_attributions"_s);
QCommandLineOption filterOption(u"filter"_s,
tr("Filter packages according to <filter> "
"(e.g. QDocModule=qtcore)"),
u"expression"_s);
QCommandLineOption baseDirOption(u"basedir"_s,
tr("Paths in documentation are made relative to this "
"directory."),
u"directory"_s);
QCommandLineOption noCheckPathsOption(
u"no-check-paths"_s,
tr("Do not check whether referenced file paths exist in basedir."));
QCommandLineOption outputOption({ u"o"_s, u"output"_s },
tr("Write generated data to <file>."),
u"file"_s);
QCommandLineOption verboseOption(u"verbose"_s, tr("Verbose output."));
QCommandLineOption silentOption({ u"s"_s, u"silent"_s }, tr("Minimal output."));
parser.addOption(generatorOption);
parser.addOption(inputFormatOption);
parser.addOption(filterOption);
parser.addOption(baseDirOption);
parser.addOption(noCheckPathsOption);
parser.addOption(outputOption);
parser.addOption(verboseOption);
parser.addOption(silentOption);
parser.process(a.arguments());
using Scanner::Checks, Scanner::Check;
Checks checks = Check::All;
checks.setFlag(Check::Paths, !parser.isSet(noCheckPathsOption));
LogLevel logLevel = NormalLog;
if (parser.isSet(verboseOption) && parser.isSet(silentOption)) {
std::cerr << qPrintable(tr("--verbose and --silent cannot be set simultaneously.")) << std::endl;
parser.showHelp(1);
}
if (parser.isSet(verboseOption))
logLevel = VerboseLog;
else if (parser.isSet(silentOption))
logLevel = SilentLog;
if (parser.positionalArguments().size() == 0)
parser.showHelp(2);
const QStringList paths = parser.positionalArguments();
QString inputFormat = parser.value(inputFormatOption);
Scanner::InputFormats formats;
if (inputFormat == "qt_attributions"_L1)
formats = Scanner::InputFormat::QtAttributions;
else if (inputFormat == "chromium_attributions"_L1)
formats = Scanner::InputFormat::ChromiumAttributions;
else if (inputFormat == "all"_L1)
formats = Scanner::InputFormat::QtAttributions | Scanner::InputFormat::ChromiumAttributions;
else {
std::cerr << qPrintable(tr("%1 is not a valid input-files argument").arg(inputFormat)) << std::endl << std::endl;
parser.showHelp(8);
}
// Parse the attribution files
QList<Package> packages;
for (const QString &path: paths) {
const QFileInfo pathInfo(path);
if (pathInfo.isDir()) {
if (logLevel == VerboseLog)
std::cerr << qPrintable(tr("Recursively scanning %1 for attribution files...").arg(
QDir::toNativeSeparators(path))) << std::endl;
std::optional<QList<Package>> p
= Scanner::scanDirectory(path, formats, logLevel);
if (!p)
return 1;
packages.append(*p);
} else if (pathInfo.isFile()) {
std::optional<QList<Package>> p = Scanner::readFile(path, logLevel);
if (!p)
return 1;
packages.append(*p);
} else {
std::cerr << qPrintable(tr("%1 is not a valid file or directory.").arg(
QDir::toNativeSeparators(path))) << std::endl << std::endl;
parser.showHelp(7);
}
}
// Apply the filter
if (parser.isSet(filterOption)) {
PackageFilter filter(parser.value(filterOption));
if (filter.type == PackageFilter::InvalidFilter)
return 4;
packages.erase(std::remove_if(packages.begin(), packages.end(),
[&filter](const Package &p) { return !filter(p); }),
packages.end());
}
// Perform the validations and checks
bool checksFailed = false;
for (auto &package : packages) {
if (!validatePackage(package, checks, logLevel))
checksFailed = true;
}
if (checksFailed)
return 1;
if (logLevel == VerboseLog)
std::cerr << qPrintable(tr("%1 packages found.").arg(packages.size())) << std::endl;
// Prepare the output text stream
QFile outFile(parser.value(outputOption));
QTextStream out(stdout);
if (!outFile.fileName().isEmpty()) {
if (!outFile.open(QFile::WriteOnly)) {
std::cerr << qPrintable(tr("Cannot open %1 for writing.").arg(
QDir::toNativeSeparators(outFile.fileName())))
<< std::endl;
return 5;
}
out.setDevice(&outFile);
}
// Generate the output and write it
QString generator = parser.value(generatorOption);
if (generator == "qdoc"_L1) {
QString baseDirectory = parser.value(baseDirOption);
if (baseDirectory.isEmpty()) {
if (paths.size() != 1) {
std::cerr << qPrintable(tr("baseDir option is not optional."));
return 1;
}
const QFileInfo pathInfo(paths.last());
if (pathInfo.isDir()) {
// include top level module name in printed paths
baseDirectory = pathInfo.dir().absoluteFilePath(u".."_s);
} else {
baseDirectory = pathInfo.absoluteDir().absoluteFilePath(u".."_s);
}
}
QDocGenerator::generate(out, packages, baseDirectory, logLevel);
} else if (generator == "json"_L1) {
JsonGenerator::generate(out, packages, logLevel);
} else {
std::cerr << qPrintable(tr("Unknown output-format %1.").arg(generator)) << std::endl;
return 6;
}
if (logLevel == VerboseLog)
std::cerr << qPrintable(tr("Processing is done.")) << std::endl;
return 0;
}