1// Copyright (C) 2022 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 <QCoreApplication>
5#include <QCommandLineParser>
6#include <QDir>
7#include <QFile>
8#include <QScopedPointer>
9
10#include <cstdlib>
11
12#include <QtQmlTypeRegistrar/private/qqmltyperegistrar_p.h>
13#include <QtQmlTypeRegistrar/private/qqmltyperegistrarutils_p.h>
14
15using namespace Qt::Literals;
16
17int main(int argc, char **argv)
18{
19 // Produce reliably the same output for the same input by disabling QHash's random seeding.
20 QHashSeed::setDeterministicGlobalSeed();
21
22 // No, you are not supposed to mess with the message pattern.
23 // Qt Creator wants to read those messages as-is and we want the convenience
24 // of QDebug to print them.
25 qputenv(varName: "QT_MESSAGE_PATTERN", value: "%{if-category}%{category}: %{endif}%{message}");
26
27 QCoreApplication app(argc, argv);
28 QCoreApplication::setApplicationName(QStringLiteral("qmltyperegistrar"));
29 QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
30
31 QCommandLineParser parser;
32 parser.addHelpOption();
33 parser.addVersionOption();
34
35 QCommandLineOption outputOption(QStringLiteral("o"));
36 outputOption.setDescription(QStringLiteral("Write output to specified file."));
37 outputOption.setValueName(QStringLiteral("file"));
38 outputOption.setFlags(QCommandLineOption::ShortOptionStyle);
39 parser.addOption(commandLineOption: outputOption);
40
41 QCommandLineOption privateIncludesOption(
42 QStringLiteral("private-includes"),
43 QStringLiteral("Include headers ending in \"_p.h\" using \"#include <private/foo_p.h>\""
44 "rather than \"#include <foo_p.h>\"."));
45 parser.addOption(commandLineOption: privateIncludesOption);
46
47 QCommandLineOption importNameOption(QStringLiteral("import-name"));
48 importNameOption.setDescription(QStringLiteral("Name of the module to use for type and module "
49 "registrations."));
50 importNameOption.setValueName(QStringLiteral("module name"));
51 parser.addOption(commandLineOption: importNameOption);
52
53 QCommandLineOption pastMajorVersionOption(QStringLiteral("past-major-version"));
54 pastMajorVersionOption.setDescription(
55 QStringLiteral("Past major version to use for type and module "
56 "registrations."));
57 pastMajorVersionOption.setValueName(QStringLiteral("past major version"));
58 parser.addOption(commandLineOption: pastMajorVersionOption);
59
60 QCommandLineOption majorVersionOption(QStringLiteral("major-version"));
61 majorVersionOption.setDescription(QStringLiteral("Major version to use for type and module "
62 "registrations."));
63 majorVersionOption.setValueName(QStringLiteral("major version"));
64 parser.addOption(commandLineOption: majorVersionOption);
65
66 QCommandLineOption minorVersionOption(QStringLiteral("minor-version"));
67 minorVersionOption.setDescription(QStringLiteral("Minor version to use for module "
68 "registration."));
69 minorVersionOption.setValueName(QStringLiteral("minor version"));
70 parser.addOption(commandLineOption: minorVersionOption);
71
72 QCommandLineOption namespaceOption(QStringLiteral("namespace"));
73 namespaceOption.setDescription(QStringLiteral("Generate type registration functions "
74 "into a C++ namespace."));
75 namespaceOption.setValueName(QStringLiteral("namespace"));
76 parser.addOption(commandLineOption: namespaceOption);
77
78 QCommandLineOption pluginTypesOption(QStringLiteral("generate-qmltypes"));
79 pluginTypesOption.setDescription(QStringLiteral("Generate qmltypes into specified file."));
80 pluginTypesOption.setValueName(QStringLiteral("qmltypes file"));
81 parser.addOption(commandLineOption: pluginTypesOption);
82
83 QCommandLineOption foreignTypesOption(QStringLiteral("foreign-types"));
84 foreignTypesOption.setDescription(
85 QStringLiteral("Comma separated list of other modules' metatypes files "
86 "to consult for foreign types when generating "
87 "qmltypes file."));
88 foreignTypesOption.setValueName(QStringLiteral("foreign types"));
89 parser.addOption(commandLineOption: foreignTypesOption);
90
91 QCommandLineOption followForeignVersioningOption(QStringLiteral("follow-foreign-versioning"));
92 followForeignVersioningOption.setDescription(
93 QStringLiteral("If this option is set the versioning scheme of foreign base classes "
94 "will be respected instead of ignored. Mostly useful for modules who "
95 "want to follow Qt's versioning scheme."));
96 parser.addOption(commandLineOption: followForeignVersioningOption);
97
98 QCommandLineOption jsroot(QStringLiteral("jsroot"));
99 jsroot.setDescription(
100 QStringLiteral("Use the JavaScript root object's meta types as sole input and do not "
101 "generate any C++ output. Only useful in combination with "
102 "--generate-qmltypes"));
103 parser.addOption(commandLineOption: jsroot);
104
105 QCommandLineOption extract(u"extract"_s);
106 extract.setDescription(
107 u"Extract QML types from a module and use QML_FOREIGN to register them"_s);
108 parser.addOption(commandLineOption: extract);
109
110 QCommandLineOption mergeQtConf("merge-qt-conf"_L1);
111 mergeQtConf.setValueName("qtconf list"_L1);
112 mergeQtConf.setFlags(QCommandLineOption::HiddenFromHelp);
113 parser.addOption(commandLineOption: mergeQtConf);
114
115 parser.addPositionalArgument(QStringLiteral("[MOC generated json file]"),
116 QStringLiteral("MOC generated json output."));
117
118 QStringList arguments;
119 if (!QmlTypeRegistrar::argumentsFromCommandLineAndFile(allArguments&: arguments, arguments: app.arguments()))
120 return EXIT_FAILURE;
121
122 parser.process(arguments);
123
124 if (parser.isSet(option: mergeQtConf)) {
125 return mergeQtConfFiles(pathToList: parser.value(option: mergeQtConf));
126 }
127
128 const QString module = parser.value(option: importNameOption);
129
130 const QLatin1String jsrootMetaTypes
131 = QLatin1String(":/qt-project.org/meta_types/jsroot_metatypes.json");
132 QStringList files = parser.positionalArguments();
133 if (parser.isSet(option: jsroot)) {
134 if (parser.isSet(option: extract)) {
135 error(fileName: module) << "If --jsroot is passed, no type registrations can be extracted.";
136 return EXIT_FAILURE;
137 }
138 if (parser.isSet(option: outputOption)) {
139 error(fileName: module) << "If --jsroot is passed, no C++ output can be generated.";
140 return EXIT_FAILURE;
141 }
142 if (!files.isEmpty() || parser.isSet(option: foreignTypesOption)) {
143 error(fileName: module) << "If --jsroot is passed, no further metatypes can be processed.";
144 return EXIT_FAILURE;
145 }
146
147 files.append(t: jsrootMetaTypes);
148 }
149
150 MetaTypesJsonProcessor processor(parser.isSet(option: privateIncludesOption));
151 if (!processor.processTypes(files))
152 return EXIT_FAILURE;
153
154 if (parser.isSet(option: extract)) {
155 if (!parser.isSet(option: outputOption)) {
156 error(fileName: module) << "The output file name must be provided";
157 return EXIT_FAILURE;
158 }
159 QString baseName = parser.value(option: outputOption);
160 return QmlTypeRegistrar::runExtract(baseName, nameSpace: parser.value(option: namespaceOption), processor);
161 }
162
163 processor.postProcessTypes();
164
165 if (!parser.isSet(option: jsroot)) {
166 processor.processForeignTypes(foreignTypesFile: jsrootMetaTypes);
167 if (parser.isSet(option: foreignTypesOption))
168 processor.processForeignTypes(foreignTypesFiles: parser.value(option: foreignTypesOption).split(sep: QLatin1Char(',')));
169 }
170
171 processor.postProcessForeignTypes();
172
173 QmlTypeRegistrar typeRegistrar;
174 typeRegistrar.setIncludes(processor.includes());
175 typeRegistrar.setModuleNameAndNamespace(module, targetNamespace: parser.value(option: namespaceOption));
176 QTypeRevision moduleVersion = QTypeRevision::fromVersion(
177 majorVersion: parser.value(option: majorVersionOption).toInt(), minorVersion: parser.value(option: minorVersionOption).toInt());
178 QList<quint8> pastMajorVersions;
179 for (const auto &x : parser.values(option: pastMajorVersionOption))
180 pastMajorVersions.append(t: x.toUInt());
181
182 typeRegistrar.setModuleVersions(moduleVersion, pastMajorVersions,
183 followForeignVersioning: parser.isSet(option: followForeignVersioningOption));
184 typeRegistrar.setTypes(types: processor.types(), foreignTypes: processor.foreignTypes());
185
186 if (!parser.isSet(option: jsroot)) {
187 if (module.isEmpty()) {
188 warning(fileName: module) << "The module name is empty. Cannot generate C++ code";
189 } else if (parser.isSet(option: outputOption)) {
190 // extract does its own file handling
191 QString outputName = parser.value(option: outputOption);
192 QFile file(outputName);
193 if (!file.open(flags: QIODeviceBase::WriteOnly)) {
194 error(fileName: QDir::toNativeSeparators(pathName: outputName))
195 << "Cannot open file for writing:" << file.errorString();
196 return EXIT_FAILURE;
197 }
198 QTextStream output(&file);
199 typeRegistrar.write(os&: output, outFileName: outputName);
200 } else {
201 QTextStream output(stdout);
202 typeRegistrar.write(os&: output, outFileName: "stdout");
203 }
204 }
205
206 if (!parser.isSet(option: pluginTypesOption))
207 return EXIT_SUCCESS;
208
209 typeRegistrar.setReferencedTypes(processor.referencedTypes());
210 typeRegistrar.setUsingDeclarations(processor.usingDeclarations());
211 const QString qmltypes = parser.value(option: pluginTypesOption);
212 if (!typeRegistrar.generatePluginTypes(pluginTypesFile: qmltypes, generatingJSRoot: parser.isSet(option: jsroot))) {
213 error(fileName: qmltypes) << "Cannot generate qmltypes file";
214 return EXIT_FAILURE;
215 }
216 return EXIT_SUCCESS;
217}
218

source code of qtdeclarative/tools/qmltyperegistrar/qmltyperegistrar.cpp