| 1 | // Copyright (C) 2024 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 <QCommandLineParser> |
| 5 | #include <QCoreApplication> |
| 6 | #include <QDir> |
| 7 | #include <QFile> |
| 8 | #include <QFileInfo> |
| 9 | #include <QJsonArray> |
| 10 | #include <QJsonDocument> |
| 11 | #include <QJsonObject> |
| 12 | |
| 13 | #include <private/qqmljscompilerstats_p.h> |
| 14 | #include <private/qqmljscompilerstatsreporter_p.h> |
| 15 | |
| 16 | using namespace Qt::Literals::StringLiterals; |
| 17 | |
| 18 | bool saveFormattedStats(const QString &stats, const QString &outputPath) |
| 19 | { |
| 20 | QString directory = QFileInfo(outputPath).dir().path(); |
| 21 | if (!QDir().mkpath(dirPath: directory)) { |
| 22 | qDebug() << "Could not ensure the existence of" << directory; |
| 23 | return false; |
| 24 | } |
| 25 | |
| 26 | QFile outputFile(outputPath); |
| 27 | if (!outputFile.open(flags: QIODevice::Text | QIODevice::WriteOnly)) { |
| 28 | qDebug() << "Could not open file" << outputPath; |
| 29 | return false; |
| 30 | } |
| 31 | |
| 32 | if (outputFile.write(data: stats.toLatin1()) == -1) { |
| 33 | qDebug() << "Could not write formatted AOT stats to" << outputPath; |
| 34 | return false; |
| 35 | } else { |
| 36 | qDebug() << "Formatted AOT stats saved to" << outputPath; |
| 37 | } |
| 38 | |
| 39 | return true; |
| 40 | } |
| 41 | |
| 42 | int main(int argc, char **argv) |
| 43 | { |
| 44 | QCoreApplication app(argc, argv); |
| 45 | QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR)); |
| 46 | |
| 47 | QCommandLineParser parser; |
| 48 | parser.addHelpOption(); |
| 49 | parser.setApplicationDescription("Internal development tool." ); |
| 50 | parser.addPositionalArgument(name: "mode" , description: "Choose whether to aggregate or display aotstats files" , |
| 51 | syntax: "[aggregate|format]" ); |
| 52 | parser.addPositionalArgument(name: "input" , description: "Aggregate mode: the aotstatslist file to aggregate. " |
| 53 | "Format mode: the aotstats file to display." ); |
| 54 | parser.addPositionalArgument(name: "output" , description: "Aggregate mode: the path where to store the " |
| 55 | "aggregated aotstats. Format mode: the the path where " |
| 56 | "the formatted output will be saved." ); |
| 57 | QCommandLineOption emptyModulesOption("empty-modules" , QCoreApplication::translate(context: "main" , key: "Format mode: File containing a list of modules with no QML files." ), "file" ); |
| 58 | parser.addOption(commandLineOption: emptyModulesOption); |
| 59 | QCommandLineOption onlyBytecodeModulesOption("only-bytecode-modules" , QCoreApplication::translate(context: "main" , key: "Format mode: File containing a list of modules for which only the bytecode is generated." ), "file" ); |
| 60 | parser.addOption(commandLineOption: onlyBytecodeModulesOption); |
| 61 | parser.process(app); |
| 62 | |
| 63 | const auto &positionalArgs = parser.positionalArguments(); |
| 64 | if (positionalArgs.size() != 3) { |
| 65 | qDebug().noquote() << parser.helpText(); |
| 66 | return EXIT_FAILURE; |
| 67 | } |
| 68 | |
| 69 | const auto &mode = positionalArgs.first(); |
| 70 | if (mode == u"aggregate"_s ) { |
| 71 | const auto aggregated = QQmlJS::AotStats::aggregateAotstatsList(aotstatsListPath: positionalArgs[1]); |
| 72 | if (!aggregated.has_value()) |
| 73 | return EXIT_FAILURE; |
| 74 | if (!aggregated->saveToDisk(filepath: positionalArgs[2])) |
| 75 | return EXIT_FAILURE; |
| 76 | |
| 77 | } else if (mode == u"format"_s ) { |
| 78 | const auto aotstats = QQmlJS::AotStats::parseAotstatsFile(aotstatsPath: positionalArgs[1]); |
| 79 | if (!aotstats.has_value()) |
| 80 | return EXIT_FAILURE; |
| 81 | |
| 82 | const auto emptyModules = parser.isSet(option: emptyModulesOption) |
| 83 | ? QQmlJS::AotStats::readAllLines(path: parser.value(option: emptyModulesOption)) |
| 84 | : QStringList(); |
| 85 | const auto onlyBytecodeModules = parser.isSet(option: onlyBytecodeModulesOption) |
| 86 | ? QQmlJS::AotStats::readAllLines(path: parser.value(option: onlyBytecodeModulesOption)) |
| 87 | : QStringList(); |
| 88 | if (!emptyModules || !onlyBytecodeModules) |
| 89 | return EXIT_FAILURE; |
| 90 | |
| 91 | const QQmlJS::AotStatsReporter reporter(aotstats.value(), emptyModules.value(), |
| 92 | onlyBytecodeModules.value()); |
| 93 | if (!saveFormattedStats(stats: reporter.format(), outputPath: positionalArgs[2])) |
| 94 | return EXIT_FAILURE; |
| 95 | } |
| 96 | |
| 97 | return EXIT_SUCCESS; |
| 98 | } |
| 99 | |