1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "jsongenerator.h"
5#include "logging.h"
6#include "packagefilter.h"
7#include "qdocgenerator.h"
8#include "scanner.h"
9
10#include <QtCore/qcommandlineparser.h>
11#include <QtCore/qcoreapplication.h>
12#include <QtCore/qdir.h>
13#include <QtCore/qfile.h>
14
15#include <iostream>
16
17using namespace Qt::Literals::StringLiterals;
18
19int main(int argc, char *argv[])
20{
21 QCoreApplication a(argc, argv);
22 a.setApplicationName(u"Qt Attributions Scanner"_s);
23 a.setApplicationVersion(u"1.2"_s);
24
25 QCommandLineParser parser;
26 parser.setApplicationDescription(tr(key: "Processes attribution files in Qt sources."));
27 parser.addPositionalArgument(name: u"path"_s,
28 description: tr(key: "Path to a qt_attribution.json/README.chromium file, "
29 "or a directory to be scannned recursively."));
30 parser.addHelpOption();
31 parser.addVersionOption();
32
33 QCommandLineOption generatorOption(u"output-format"_s,
34 tr(key: "Output format (\"qdoc\", \"json\")."),
35 u"generator"_s, u"qdoc"_s);
36 QCommandLineOption inputFormatOption(u"input-files"_s,
37 tr(key: "Input files (\"qt_attributions\" scans for "
38 "qt_attribution.json, \"chromium_attributions\" for "
39 "README.Chromium, \"all\" for both)."),
40 u"input_format"_s,
41 u"qt_attributions"_s);
42 QCommandLineOption filterOption(u"filter"_s,
43 tr(key: "Filter packages according to <filter> "
44 "(e.g. QDocModule=qtcore)"),
45 u"expression"_s);
46 QCommandLineOption baseDirOption(u"basedir"_s,
47 tr(key: "Paths in documentation are made relative to this "
48 "directory."),
49 u"directory"_s);
50 QCommandLineOption noCheckPathsOption(
51 u"no-check-paths"_s,
52 tr(key: "Do not check whether referenced file paths exist in basedir."));
53 QCommandLineOption outputOption({ u"o"_s, u"output"_s },
54 tr(key: "Write generated data to <file>."),
55 u"file"_s);
56 QCommandLineOption verboseOption(u"verbose"_s, tr(key: "Verbose output."));
57 QCommandLineOption silentOption({ u"s"_s, u"silent"_s }, tr(key: "Minimal output."));
58
59 parser.addOption(commandLineOption: generatorOption);
60 parser.addOption(commandLineOption: inputFormatOption);
61 parser.addOption(commandLineOption: filterOption);
62 parser.addOption(commandLineOption: baseDirOption);
63 parser.addOption(commandLineOption: noCheckPathsOption);
64 parser.addOption(commandLineOption: outputOption);
65 parser.addOption(commandLineOption: verboseOption);
66 parser.addOption(commandLineOption: silentOption);
67
68 parser.process(arguments: a.arguments());
69
70 using Scanner::Checks, Scanner::Check;
71 Checks checks = Check::All;
72 checks.setFlag(flag: Check::Paths, on: !parser.isSet(option: noCheckPathsOption));
73
74 LogLevel logLevel = NormalLog;
75 if (parser.isSet(option: verboseOption) && parser.isSet(option: silentOption)) {
76 std::cerr << qPrintable(tr("--verbose and --silent cannot be set simultaneously.")) << std::endl;
77 parser.showHelp(exitCode: 1);
78 }
79
80 if (parser.isSet(option: verboseOption))
81 logLevel = VerboseLog;
82 else if (parser.isSet(option: silentOption))
83 logLevel = SilentLog;
84
85 if (parser.positionalArguments().size() != 1)
86 parser.showHelp(exitCode: 2);
87
88 const QString path = parser.positionalArguments().constLast();
89
90 QString inputFormat = parser.value(option: inputFormatOption);
91 Scanner::InputFormats formats;
92 if (inputFormat == "qt_attributions"_L1)
93 formats = Scanner::InputFormat::QtAttributions;
94 else if (inputFormat == "chromium_attributions"_L1)
95 formats = Scanner::InputFormat::ChromiumAttributions;
96 else if (inputFormat == "all"_L1)
97 formats = Scanner::InputFormat::QtAttributions | Scanner::InputFormat::ChromiumAttributions;
98 else {
99 std::cerr << qPrintable(tr("%1 is not a valid input-files argument").arg(inputFormat)) << std::endl << std::endl;
100 parser.showHelp(exitCode: 8);
101 }
102
103 // Parse the attribution files
104 QList<Package> packages;
105 const QFileInfo pathInfo(path);
106 if (pathInfo.isDir()) {
107 if (logLevel == VerboseLog)
108 std::cerr << qPrintable(tr("Recursively scanning %1 for attribution files...").arg(
109 QDir::toNativeSeparators(path))) << std::endl;
110 std::optional<QList<Package>> p = Scanner::scanDirectory(directory: path, inputFormats: formats, checks, logLevel);
111 if (!p)
112 return 1;
113 packages = *p;
114 } else if (pathInfo.isFile()) {
115 std::optional<QList<Package>> p = Scanner::readFile(filePath: path, checks, logLevel);
116 if (!p)
117 return 1;
118 packages = *p;
119
120 } else {
121 std::cerr << qPrintable(tr("%1 is not a valid file or directory.").arg(
122 QDir::toNativeSeparators(path))) << std::endl << std::endl;
123 parser.showHelp(exitCode: 7);
124 }
125
126 // Apply the filter
127 if (parser.isSet(option: filterOption)) {
128 PackageFilter filter(parser.value(option: filterOption));
129 if (filter.type == PackageFilter::InvalidFilter)
130 return 4;
131 packages.erase(abegin: std::remove_if(first: packages.begin(), last: packages.end(),
132 pred: [&filter](const Package &p) { return !filter(p); }),
133 aend: packages.end());
134 }
135
136 if (logLevel == VerboseLog)
137 std::cerr << qPrintable(tr("%1 packages found.").arg(packages.size())) << std::endl;
138
139 // Prepare the output text stream
140 QFile outFile(parser.value(option: outputOption));
141 QTextStream out(stdout);
142 if (!outFile.fileName().isEmpty()) {
143 if (!outFile.open(flags: QFile::WriteOnly)) {
144 std::cerr << qPrintable(tr("Cannot open %1 for writing.").arg(
145 QDir::toNativeSeparators(outFile.fileName())))
146 << std::endl;
147 return 5;
148 }
149 out.setDevice(&outFile);
150 }
151
152 // Generate the output and write it
153 QString generator = parser.value(option: generatorOption);
154 if (generator == "qdoc"_L1) {
155 QString baseDirectory = parser.value(option: baseDirOption);
156 if (baseDirectory.isEmpty()) {
157 if (pathInfo.isDir()) {
158 // include top level module name in printed paths
159 baseDirectory = pathInfo.dir().absoluteFilePath(fileName: u".."_s);
160 } else {
161 baseDirectory = pathInfo.absoluteDir().absoluteFilePath(fileName: u".."_s);
162 }
163 }
164
165 QDocGenerator::generate(out, packages, baseDirectory, logLevel);
166 } else if (generator == "json"_L1) {
167 JsonGenerator::generate(out, packages, logLevel);
168 } else {
169 std::cerr << qPrintable(tr("Unknown output-format %1.").arg(generator)) << std::endl;
170 return 6;
171 }
172
173 if (logLevel == VerboseLog)
174 std::cerr << qPrintable(tr("Processing is done.")) << std::endl;
175
176 return 0;
177}
178

source code of qttools/src/qtattributionsscanner/main.cpp