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 "jsongenerator.h"
30 #include "logging.h"
31 #include "packagefilter.h"
32 #include "qdocgenerator.h"
33 #include "scanner.h"
34 
35 #include <QtCore/qcommandlineparser.h>
36 #include <QtCore/qcoreapplication.h>
37 #include <QtCore/qdir.h>
38 #include <QtCore/qfile.h>
39 
40 #include <iostream>
41 
42 
main(int argc,char * argv[])43 int main(int argc, char *argv[])
44 {
45     QCoreApplication a(argc, argv);
46     a.setApplicationName(QStringLiteral("Qt Attributions Scanner"));
47     a.setApplicationVersion(QStringLiteral("1.1"));
48 
49     QCommandLineParser parser;
50     parser.setApplicationDescription(tr("Processes attribution files in Qt sources."));
51     parser.addPositionalArgument(QStringLiteral("path"),
52                                  tr("Path to a qt_attribution.json/README.chromium file, "
53                                     "or a directory to be scannned recursively."));
54     parser.addHelpOption();
55     parser.addVersionOption();
56 
57     QCommandLineOption generatorOption(QStringLiteral("output-format"),
58                                        tr("Output format (\"qdoc\", \"json\")."),
59                                        QStringLiteral("generator"),
60                                        QStringLiteral("qdoc"));
61     QCommandLineOption inputFormatOption(QStringLiteral("input-files"),
62                                        tr("Input files (\"qt_attributions\" scans for qt_attribution.json, "
63                                           "\"chromium_attributions\" for README.Chromium, \"all\" for both)."),
64                                        QStringLiteral("input_format"),
65                                        QStringLiteral("qt_attributions"));
66     QCommandLineOption filterOption(QStringLiteral("filter"),
67                                     tr("Filter packages according to <filter> (e.g. QDocModule=qtcore)"),
68                                     QStringLiteral("expression"));
69     QCommandLineOption baseDirOption(QStringLiteral("basedir"),
70                                      tr("Paths in documentation are made relative to this "
71                                         "directory."),
72                                      QStringLiteral("directory"));
73     QCommandLineOption outputOption({ QStringLiteral("o"), QStringLiteral("output") },
74                                     tr("Write generated data to <file>."),
75                                     QStringLiteral("file"));
76     QCommandLineOption verboseOption(QStringLiteral("verbose"),
77                                      tr("Verbose output."));
78     QCommandLineOption silentOption({ QStringLiteral("s"), QStringLiteral("silent") },
79                                     tr("Minimal output."));
80 
81     parser.addOption(generatorOption);
82     parser.addOption(inputFormatOption);
83     parser.addOption(filterOption);
84     parser.addOption(baseDirOption);
85     parser.addOption(outputOption);
86     parser.addOption(verboseOption);
87     parser.addOption(silentOption);
88 
89     parser.process(a.arguments());
90 
91     LogLevel logLevel = NormalLog;
92     if (parser.isSet(verboseOption) && parser.isSet(silentOption)) {
93         std::cerr << qPrintable(tr("--verbose and --silent cannot be set simultaneously.")) << std::endl;
94         parser.showHelp(1);
95     }
96 
97     if (parser.isSet(verboseOption))
98         logLevel = VerboseLog;
99     else if (parser.isSet(silentOption))
100         logLevel = SilentLog;
101 
102     if (parser.positionalArguments().size() != 1)
103         parser.showHelp(2);
104 
105     const QString path = parser.positionalArguments().constLast();
106 
107     QString inputFormat = parser.value(inputFormatOption);
108     Scanner::InputFormats formats;
109     if (inputFormat == QLatin1String("qt_attributions"))
110         formats = Scanner::InputFormat::QtAttributions;
111     else if (inputFormat == QLatin1String("chromium_attributions"))
112         formats = Scanner::InputFormat::ChromiumAttributions;
113     else if (inputFormat == QLatin1String("all"))
114         formats = Scanner::InputFormat::QtAttributions | Scanner::InputFormat::ChromiumAttributions;
115     else {
116         std::cerr << qPrintable(tr("%1 is not a valid input-files argument").arg(inputFormat)) << std::endl << std::endl;
117         parser.showHelp(8);
118     }
119 
120     // Parse the attribution files
121     QVector<Package> packages;
122     const QFileInfo pathInfo(path);
123     if (pathInfo.isDir()) {
124         if (logLevel == VerboseLog)
125             std::cerr << qPrintable(tr("Recursively scanning %1 for attribution files...").arg(
126                                         QDir::toNativeSeparators(path))) << std::endl;
127         packages = Scanner::scanDirectory(path, formats, logLevel);
128     } else if (pathInfo.isFile()) {
129         packages = Scanner::readFile(path, logLevel);
130     } else {
131         std::cerr << qPrintable(tr("%1 is not a valid file or directory.").arg(
132                                     QDir::toNativeSeparators(path))) << std::endl << std::endl;
133         parser.showHelp(7);
134     }
135 
136     // Apply the filter
137     if (parser.isSet(filterOption)) {
138         PackageFilter filter(parser.value(filterOption));
139         if (filter.type == PackageFilter::InvalidFilter)
140             return 4;
141         packages.erase(std::remove_if(packages.begin(), packages.end(),
142                                       [&filter](const Package &p) { return !filter(p); }),
143                        packages.end());
144     }
145 
146     if (logLevel == VerboseLog)
147         std::cerr << qPrintable(tr("%1 packages found.").arg(packages.size())) << std::endl;
148 
149     // Prepare the output text stream
150     QTextStream out(stdout);
151     QFile outFile(parser.value(outputOption));
152     if (!outFile.fileName().isEmpty()) {
153         if (!outFile.open(QFile::WriteOnly)) {
154             std::cerr << qPrintable(tr("Cannot open %1 for writing.").arg(
155                                         QDir::toNativeSeparators(outFile.fileName())))
156                       << std::endl;
157             return 5;
158         }
159         out.setDevice(&outFile);
160     }
161 
162     // Generate the output and write it
163     QString generator = parser.value(generatorOption);
164     out.setCodec("UTF-8");
165     if (generator == QLatin1String("qdoc")) {
166         QString baseDirectory = parser.value(baseDirOption);
167         if (baseDirectory.isEmpty()) {
168             if (pathInfo.isDir()) {
169                 // include top level module name in printed paths
170                 baseDirectory = pathInfo.dir().absoluteFilePath(QStringLiteral(".."));
171             } else {
172                 baseDirectory = pathInfo.absoluteDir().absoluteFilePath(QStringLiteral(".."));
173             }
174         }
175 
176         QDocGenerator::generate(out, packages, baseDirectory, logLevel);
177     } else if (generator == QLatin1String("json")) {
178         JsonGenerator::generate(out, packages, logLevel);
179     } else {
180         std::cerr << qPrintable(tr("Unknown output-format %1.").arg(generator)) << std::endl;
181         return 6;
182     }
183 
184     if (logLevel == VerboseLog)
185         std::cerr << qPrintable(tr("Processing is done.")) << std::endl;
186 
187     return 0;
188 }
189