1// Copyright (C) 2017-2020 Ford Motor Company
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include <qcommandlineoption.h>
5#include <qcommandlineparser.h>
6#include <qcoreapplication.h>
7#include <qfileinfo.h>
8#include <qjsondocument.h>
9#include <qjsonobject.h>
10#include <qjsonarray.h>
11
12#include "cppcodegenerator.h"
13#include "repcodegenerator.h"
14#include "repparser.h"
15#include "utils.h"
16
17#include <cstdio>
18
19#define PROGRAM_NAME "repc"
20#define REPC_VERSION "1.0.0"
21
22enum Mode {
23 InRep = 1,
24 InJson = 2,
25 OutRep = 4,
26 OutSource = 8,
27 OutReplica = 16,
28 OutMerged = OutSource | OutReplica
29};
30
31static const QLatin1String REP("rep");
32
33QT_USE_NAMESPACE
34
35int main(int argc, char **argv)
36{
37 QCoreApplication app(argc, argv);
38 QCoreApplication::setApplicationVersion(QString::fromLatin1(REPC_VERSION));
39
40 QString outputFile;
41 QString inputFile;
42 int mode = 0;
43 QCommandLineParser parser;
44 parser.setApplicationDescription(QStringLiteral("repc tool v%1 (Qt %2).\n")
45 .arg(QStringLiteral(REPC_VERSION), args: QString::fromLatin1(QT_VERSION_STR)));
46 parser.addHelpOption();
47 parser.addVersionOption();
48 QCommandLineOption inputTypeOption(QStringLiteral("i"));
49 inputTypeOption.setDescription(QLatin1String("Input file type:\n"
50 "rep: replicant template files.\n"
51 "json: JSON output from moc of a Qt header file."));
52 inputTypeOption.setValueName(QStringLiteral("rep|json"));
53 parser.addOption(commandLineOption: inputTypeOption);
54
55 QCommandLineOption outputTypeOption(QStringLiteral("o"));
56 outputTypeOption.setDescription(QLatin1String("Output file type:\n"
57 "source: generates source header. Is incompatible with \"-i src\" option.\n"
58 "replica: generates replica header.\n"
59 "merged: generates combined replica/source header.\n"
60 "rep: generates replicant template file from C++ QOject classes. Is not compatible with \"-i rep\" option."));
61 outputTypeOption.setValueName(QStringLiteral("source|replica|merged|rep"));
62 parser.addOption(commandLineOption: outputTypeOption);
63
64 QCommandLineOption includePathOption(QStringLiteral("I"));
65 includePathOption.setDescription(QStringLiteral("Add dir to the include path for header files. This parameter is needed only if the input file type is src (.h file)."));
66 includePathOption.setValueName(QStringLiteral("dir"));
67 parser.addOption(commandLineOption: includePathOption);
68
69 QCommandLineOption alwaysClassOption(QStringLiteral("c"));
70 alwaysClassOption.setDescription(QStringLiteral("Always output `class` type for .rep files and never `POD`."));
71 parser.addOption(commandLineOption: alwaysClassOption);
72
73 QCommandLineOption debugOption(QStringLiteral("d"));
74 debugOption.setDescription(QStringLiteral("Print out parsing debug information (for troubleshooting)."));
75 parser.addOption(commandLineOption: debugOption);
76
77 parser.addPositionalArgument(QStringLiteral("[json-file/rep-file]"),
78 QStringLiteral("Input json/rep file to read from, otherwise stdin."));
79
80 parser.addPositionalArgument(QStringLiteral("[rep-file/header-file]"),
81 QStringLiteral("Output header/rep file to write to, otherwise stdout."));
82
83 parser.process(arguments: app.arguments());
84
85 const QStringList files = parser.positionalArguments();
86
87 if (files.size() > 2) {
88 fprintf(stderr, format: "%s", qPrintable(QLatin1String(PROGRAM_NAME ": Too many input, output files specified: '") + files.join(QStringLiteral("' '")) + QStringLiteral("\'.\n")));
89 parser.showHelp(exitCode: 1);
90 }
91
92 if (parser.isSet(option: inputTypeOption)) {
93 const QString &inputType = parser.value(option: inputTypeOption);
94 if (inputType == REP)
95 mode = InRep;
96 else if (inputType == u"json")
97 mode = InJson;
98 else {
99 fprintf(stderr, PROGRAM_NAME ": Unknown input type\"%s\".\n", qPrintable(inputType));
100 parser.showHelp(exitCode: 1);
101 }
102 }
103
104 if (parser.isSet(option: outputTypeOption)) {
105 const QString &outputType = parser.value(option: outputTypeOption);
106 if (outputType == REP)
107 mode |= OutRep;
108 else if (outputType == u"replica")
109 mode |= OutReplica;
110 else if (outputType == u"source")
111 mode |= OutSource;
112 else if (outputType == u"merged")
113 mode |= OutMerged;
114 else {
115 fprintf(stderr, PROGRAM_NAME ": Unknown output type\"%s\".\n", qPrintable(outputType));
116 parser.showHelp(exitCode: 1);
117 }
118 }
119
120 switch (files.size()) {
121 case 2:
122 outputFile = files.last();
123 if (!(mode & (OutRep | OutSource | OutReplica))) {
124 // try to figure out the Out mode from file extension
125 if (outputFile.endsWith(s: QLatin1String(".rep")))
126 mode |= OutRep;
127 }
128 Q_FALLTHROUGH();
129 case 1:
130 inputFile = files.first();
131 if (!(mode & (InRep | InJson))) {
132 // try to figure out the In mode from file extension
133 if (inputFile.endsWith(s: QLatin1String(".rep")))
134 mode |= InRep;
135 else
136 mode |= InJson;
137 }
138 break;
139 }
140 // check mode sanity
141 if (!(mode & (InRep | InJson))) {
142 fprintf(stderr, PROGRAM_NAME ": Unknown input type, please use -i option to specify one.\n");
143 parser.showHelp(exitCode: 1);
144 }
145 if (!(mode & (OutRep | OutSource | OutReplica))) {
146 fprintf(stderr, PROGRAM_NAME ": Unknown output type, please use -o option to specify one.\n");
147 parser.showHelp(exitCode: 1);
148 }
149 if (mode & InRep && mode & OutRep) {
150 fprintf(stderr, PROGRAM_NAME ": Invalid input/output type combination, both are rep files.\n");
151 parser.showHelp(exitCode: 1);
152 }
153 if (mode & InJson && mode & OutSource) {
154 fprintf(stderr, PROGRAM_NAME ": Invalid input/output type combination, both are source header files.\n");
155 parser.showHelp(exitCode: 1);
156 }
157
158 QFile input;
159 if (inputFile.isEmpty()) {
160 inputFile = QStringLiteral("<stdin>");
161 input.open(stdin, ioFlags: QIODevice::ReadOnly);
162 } else {
163 input.setFileName(inputFile);
164 if (!input.open(flags: QIODevice::ReadOnly)) {
165 fprintf(stderr, PROGRAM_NAME ": %s: No such file.\n", qPrintable(inputFile));
166 return 1;
167 }
168 }
169
170 QFile output;
171 if (outputFile.isEmpty()) {
172 output.open(stdout, ioFlags: QIODevice::WriteOnly);
173 } else {
174 output.setFileName(outputFile);
175 if (!output.open(flags: QIODevice::WriteOnly)) {
176 fprintf(stderr, PROGRAM_NAME ": could not open output file '%s': %s.\n",
177 qPrintable(outputFile), qPrintable(output.errorString()));
178 return 1;
179 }
180 }
181
182 if (mode & InJson) {
183 QJsonDocument doc(QJsonDocument::fromJson(json: input.readAll()));
184 input.close();
185 if (!doc.isObject()) {
186 fprintf(stderr, PROGRAM_NAME ": Unable to read json input.\n");
187 return 0;
188 }
189
190 QJsonObject json = doc.object();
191
192 if (!json.contains(key: QLatin1String("classes")) || !json[QLatin1String("classes")].isArray()) {
193 fprintf(stderr, PROGRAM_NAME ": No QObject classes found.\n");
194 return 0;
195 }
196
197 QJsonArray classes = json[QLatin1String("classes")].toArray();
198
199 if (mode & OutRep) {
200 CppCodeGenerator generator(&output);
201 generator.generate(classList: classes, alwaysGenerateClass: parser.isSet(option: alwaysClassOption));
202 } else {
203 Q_ASSERT(mode & OutReplica);
204 RepCodeGenerator generator(&output, classList2AST(classes));
205 generator.generate(mode: RepCodeGenerator::REPLICA, fileName: outputFile);
206 }
207 } else {
208 Q_ASSERT(!(mode & OutRep));
209 RepParser repparser(input);
210 if (parser.isSet(option: debugOption))
211 repparser.setDebug();
212 if (!repparser.parse()) {
213 fprintf(stderr, PROGRAM_NAME ": %s:%d: error: %s\n", qPrintable(inputFile), repparser.lineNumber(), qPrintable(repparser.errorString()));
214 // if everything is okay and only the input was malformed => remove the output file
215 // let's not create an empty file -- make sure the build system tries to run repc again
216 // this is the same behavior other code generators exhibit (e.g. flex)
217 output.remove();
218 return 1;
219 }
220
221 input.close();
222
223 RepCodeGenerator generator(&output, repparser.ast());
224 if ((mode & OutMerged) == OutMerged)
225 generator.generate(mode: RepCodeGenerator::MERGED, fileName: outputFile);
226 else if (mode & OutReplica)
227 generator.generate(mode: RepCodeGenerator::REPLICA, fileName: outputFile);
228 else if (mode & OutSource)
229 generator.generate(mode: RepCodeGenerator::SOURCE, fileName: outputFile);
230 else {
231 fprintf(stderr, PROGRAM_NAME ": Unknown mode.\n");
232 return 1;
233 }
234 }
235
236 output.close();
237 return 0;
238}
239

source code of qtremoteobjects/tools/repc/main.cpp