/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace QQmlJS; static bool argumentsFromCommandLineAndFile(QStringList& allArguments, const QStringList &arguments) { allArguments.reserve(arguments.size()); for (const QString &argument : arguments) { // "@file" doesn't start with a '-' so we can't use QCommandLineParser for it if (argument.startsWith(QLatin1Char('@'))) { QString optionsFile = argument; optionsFile.remove(0, 1); if (optionsFile.isEmpty()) { fprintf(stderr, "The @ option requires an input file"); return false; } QFile f(optionsFile); if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { fprintf(stderr, "Cannot open options file specified with @"); return false; } while (!f.atEnd()) { QString line = QString::fromLocal8Bit(f.readLine().trimmed()); if (!line.isEmpty()) allArguments << line; } } else { allArguments << argument; } } return true; } int main(int argc, char **argv) { // Produce reliably the same output for the same input by disabling QHash's random seeding. qSetGlobalQHashSeed(0); QCoreApplication app(argc, argv); QCoreApplication::setApplicationName(QStringLiteral("qmlcachegen")); QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR)); QCommandLineParser parser; parser.addHelpOption(); parser.addVersionOption(); QCommandLineOption filterResourceFileOption(QStringLiteral("filter-resource-file"), QCoreApplication::translate("main", "Filter out QML/JS files from a resource file that can be cached ahead of time instead")); parser.addOption(filterResourceFileOption); QCommandLineOption resourceFileMappingOption(QStringLiteral("resource-file-mapping"), QCoreApplication::translate("main", "Path from original resource file to new one"), QCoreApplication::translate("main", "old-name:new-name")); parser.addOption(resourceFileMappingOption); QCommandLineOption resourceOption(QStringLiteral("resource"), QCoreApplication::translate("main", "Qt resource file that might later contain one of the compiled files"), QCoreApplication::translate("main", "resource-file-name")); parser.addOption(resourceOption); QCommandLineOption resourcePathOption(QStringLiteral("resource-path"), QCoreApplication::translate("main", "Qt resource file path corresponding to the file being compiled"), QCoreApplication::translate("main", "resource-path")); parser.addOption(resourcePathOption); QCommandLineOption resourceNameOption(QStringLiteral("resource-name"), QCoreApplication::translate("main", "Required to generate qmlcache_loader without qrc files. This is the name of the Qt resource the input files belong to."), QCoreApplication::translate("main", "compiled-file-list")); parser.addOption(resourceNameOption); QCommandLineOption outputFileOption(QStringLiteral("o"), QCoreApplication::translate("main", "Output file name"), QCoreApplication::translate("main", "file name")); parser.addOption(outputFileOption); parser.addPositionalArgument(QStringLiteral("[qml file]"), QStringLiteral("QML source file to generate cache for.")); parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); QStringList arguments; if (!argumentsFromCommandLineAndFile(arguments, app.arguments())) return EXIT_FAILURE; parser.process(arguments); enum Output { GenerateCpp, GenerateCacheFile, GenerateLoader, GenerateLoaderStandAlone, } target = GenerateCacheFile; QString outputFileName; if (parser.isSet(outputFileOption)) outputFileName = parser.value(outputFileOption); if (outputFileName.endsWith(QLatin1String(".cpp"))) { target = GenerateCpp; if (outputFileName.endsWith(QLatin1String("qmlcache_loader.cpp"))) target = GenerateLoader; } if (target == GenerateLoader && parser.isSet(resourceNameOption)) target = GenerateLoaderStandAlone; const QStringList sources = parser.positionalArguments(); if (sources.isEmpty()){ parser.showHelp(); } else if (sources.count() > 1 && (target != GenerateLoader && target != GenerateLoaderStandAlone)) { fprintf(stderr, "%s\n", qPrintable(QStringLiteral("Too many input files specified: '") + sources.join(QStringLiteral("' '")) + QLatin1Char('\''))); return EXIT_FAILURE; } const QString inputFile = !sources.isEmpty() ? sources.first() : QString(); if (outputFileName.isEmpty()) outputFileName = inputFile + QLatin1Char('c'); if (parser.isSet(filterResourceFileOption)) return qRelocateResourceFile(inputFile, outputFileName); if (target == GenerateLoader) { QQmlJSResourceFileMapper mapper(sources); QQmlJSCompileError error; if (!qQmlJSGenerateLoader(mapper.qmlCompilerFiles(), outputFileName, parser.values(resourceFileMappingOption), &error.message)) { error.augment(QLatin1String("Error generating loader stub: ")).print(); return EXIT_FAILURE; } return EXIT_SUCCESS; } if (target == GenerateLoaderStandAlone) { QQmlJSCompileError error; if (!qQmlJSGenerateLoader(sources, outputFileName, parser.values(resourceNameOption), &error.message)) { error.augment(QLatin1String("Error generating loader stub: ")).print(); return EXIT_FAILURE; } return EXIT_SUCCESS; } QString inputFileUrl = inputFile; QQmlJSSaveFunction saveFunction; if (target == GenerateCpp) { QQmlJSResourceFileMapper fileMapper(parser.values(resourceOption)); QString inputResourcePath = parser.value(resourcePathOption); if (!inputResourcePath.isEmpty() && !fileMapper.isEmpty()) { fprintf(stderr, "--%s and --%s are mutually exclusive.\n", qPrintable(resourcePathOption.names().first()), qPrintable(resourceOption.names().first())); return EXIT_FAILURE; } // If the user didn't specify the resource path corresponding to the file on disk being // compiled, try to determine it from the resource file, if one was supplied. if (inputResourcePath.isEmpty()) { const QStringList resourcePaths = fileMapper.resourcePaths(inputFile); if (resourcePaths.isEmpty()) { fprintf(stderr, "No resource path for file: %s\n", qPrintable(inputFile)); return EXIT_FAILURE; } if (resourcePaths.size() != 1) { fprintf(stderr, "Multiple resource paths for file %s. " "Use the --%s option to disambiguate:\n", qPrintable(inputFile), qPrintable(resourcePathOption.names().first())); for (const QString &resourcePath: resourcePaths) fprintf(stderr, "\t%s\n", qPrintable(resourcePath)); return EXIT_FAILURE; } inputResourcePath = resourcePaths.first(); } inputFileUrl = QStringLiteral("qrc://") + inputResourcePath; saveFunction = [inputResourcePath, outputFileName]( const QV4::CompiledData::SaveableUnitPointer &unit, const QQmlJSAotFunctionMap &aotFunctions, QString *errorString) { return qSaveQmlJSUnitAsCpp(inputResourcePath, outputFileName, unit, aotFunctions, errorString); }; } else { saveFunction = [outputFileName](const QV4::CompiledData::SaveableUnitPointer &unit, const QQmlJSAotFunctionMap &aotFunctions, QString *errorString) { Q_UNUSED(aotFunctions); return unit.saveToDisk( [&outputFileName, errorString](const char *data, quint32 size) { return QV4::CompiledData::SaveableUnitPointer::writeDataToFile( outputFileName, data, size, errorString); }); }; } if (inputFile.endsWith(QLatin1String(".qml"))) { QQmlJSCompileError error; if (!qCompileQmlFile(inputFile, saveFunction, nullptr, &error)) { error.augment(QLatin1String("Error compiling qml file: ")).print(); return EXIT_FAILURE; } } else if (inputFile.endsWith(QLatin1String(".js")) || inputFile.endsWith(QLatin1String(".mjs"))) { QQmlJSCompileError error; if (!qCompileJSFile(inputFile, inputFileUrl, saveFunction, &error)) { error.augment(QLatin1String("Error compiling js file: ")).print(); return EXIT_FAILURE; } } else { fprintf(stderr, "Ignoring %s input file as it is not QML source code - maybe remove from QML_FILES?\n", qPrintable(inputFile)); } return EXIT_SUCCESS; }