| 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 | |
| 15 | using namespace Qt::Literals; |
| 16 | |
| 17 | int 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 (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 | |