aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qmlcachegen/qmlcachegen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/qmlcachegen/qmlcachegen.cpp')
-rw-r--r--tools/qmlcachegen/qmlcachegen.cpp247
1 files changed, 208 insertions, 39 deletions
diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp
index d8af157b8e..9c97ef7694 100644
--- a/tools/qmlcachegen/qmlcachegen.cpp
+++ b/tools/qmlcachegen/qmlcachegen.cpp
@@ -33,10 +33,34 @@
#include <QFileInfo>
#include <QDateTime>
#include <QHashFunctions>
+#include <QSaveFile>
#include <private/qqmlirbuilder_p.h>
#include <private/qqmljsparser_p.h>
+#include "resourcefilemapper.h"
+
+int filterResourceFile(const QString &input, const QString &output);
+bool generateLoader(const QStringList &compiledFiles, const QString &output, const QStringList &resourceFileMappings, QString *errorString);
+QString symbolNamespaceForPath(const QString &relativePath);
+
+QSet<QString> illegalNames;
+
+void setupIllegalNames()
+{
+ // #### this in incomplete
+ illegalNames.insert(QStringLiteral("Math"));
+ illegalNames.insert(QStringLiteral("Array"));
+ illegalNames.insert(QStringLiteral("String"));
+ illegalNames.insert(QStringLiteral("Function"));
+ illegalNames.insert(QStringLiteral("Boolean"));
+ illegalNames.insert(QStringLiteral("Number"));
+ illegalNames.insert(QStringLiteral("Date"));
+ illegalNames.insert(QStringLiteral("RegExp"));
+ illegalNames.insert(QStringLiteral("Error"));
+ illegalNames.insert(QStringLiteral("Object"));
+}
+
struct Error
{
QString message;
@@ -135,10 +159,11 @@ static bool checkArgumentsObjectUseInSignalHandlers(const QmlIR::Document &doc,
return true;
}
-static bool compileQmlFile(const QString &inputFileName, const QString &outputFileName, const QString &targetABI, Error *error)
+using SaveFunction = std::function<bool (QV4::CompiledData::CompilationUnit *, QString *)>;
+
+static bool compileQmlFile(const QString &inputFileName, SaveFunction saveFunction, Error *error)
{
QmlIR::Document irDocument(/*debugMode*/false);
- irDocument.jsModule.targetABI = targetABI;
QString sourceCode;
{
@@ -155,7 +180,6 @@ static bool compileQmlFile(const QString &inputFileName, const QString &outputFi
}
{
- QSet<QString> illegalNames; // ####
QmlIR::IRBuilder irBuilder(illegalNames);
if (!irBuilder.generateFromQml(sourceCode, inputFileName, &irDocument)) {
for (const QQmlJS::DiagnosticMessage &parseError: qAsConst(irBuilder.errors)) {
@@ -173,7 +197,7 @@ static bool compileQmlFile(const QString &inputFileName, const QString &outputFi
QmlIR::JSCodeGen v4CodeGen(irDocument.code,
&irDocument.jsGenerator, &irDocument.jsModule,
&irDocument.jsParserEngine, irDocument.program,
- /*import cache*/0, &irDocument.jsGenerator.stringTable);
+ /*import cache*/nullptr, &irDocument.jsGenerator.stringTable, illegalNames);
v4CodeGen.setUseFastLookups(false); // Disable lookups in non-standalone (aka QML) mode
for (QmlIR::Object *object: qAsConst(irDocument.objects)) {
if (object->functionsAndExpressions->count == 0)
@@ -210,7 +234,7 @@ static bool compileQmlFile(const QString &inputFileName, const QString &outputFi
unit->flags |= QV4::CompiledData::Unit::PendingTypeCompilation;
irDocument.javaScriptCompilationUnit->data = unit;
- if (!irDocument.javaScriptCompilationUnit->saveToDisk(outputFileName, &error->message))
+ if (!saveFunction(irDocument.javaScriptCompilationUnit, &error->message))
return false;
free(unit);
@@ -218,10 +242,9 @@ static bool compileQmlFile(const QString &inputFileName, const QString &outputFi
return true;
}
-static bool compileJSFile(const QString &inputFileName, const QString &outputFileName, const QString &targetABI, Error *error)
+static bool compileJSFile(const QString &inputFileName, const QString &inputFileUrl, SaveFunction saveFunction, Error *error)
{
QmlIR::Document irDocument(/*debugMode*/false);
- irDocument.jsModule.targetABI = targetABI;
QString sourceCode;
{
@@ -273,13 +296,13 @@ static bool compileJSFile(const QString &inputFileName, const QString &outputFil
}
{
- irDocument.jsModule.fileName = inputFileName;
QmlIR::JSCodeGen v4CodeGen(irDocument.code, &irDocument.jsGenerator,
&irDocument.jsModule, &irDocument.jsParserEngine,
- irDocument.program, /*import cache*/0,
- &irDocument.jsGenerator.stringTable);
+ irDocument.program, /*import cache*/nullptr,
+ &irDocument.jsGenerator.stringTable, illegalNames);
v4CodeGen.setUseFastLookups(false); // Disable lookups in non-standalone (aka QML) mode
- v4CodeGen.generateFromProgram(inputFileName, sourceCode, program, &irDocument.jsModule, QV4::Compiler::GlobalCode);
+ v4CodeGen.generateFromProgram(inputFileName, inputFileUrl, sourceCode, program,
+ &irDocument.jsModule, QV4::Compiler::GlobalCode);
QList<QQmlJS::DiagnosticMessage> jsErrors = v4CodeGen.errors();
if (!jsErrors.isEmpty()) {
for (const QQmlJS::DiagnosticMessage &e: qAsConst(jsErrors)) {
@@ -298,7 +321,7 @@ static bool compileJSFile(const QString &inputFileName, const QString &outputFil
unit->flags |= QV4::CompiledData::Unit::StaticData;
irDocument.javaScriptCompilationUnit->data = unit;
- if (!irDocument.javaScriptCompilationUnit->saveToDisk(outputFileName, &error->message)) {
+ if (!saveFunction(irDocument.javaScriptCompilationUnit, &error->message)) {
engine->setDirectives(oldDirs);
return false;
}
@@ -309,6 +332,81 @@ static bool compileJSFile(const QString &inputFileName, const QString &outputFil
return true;
}
+static bool saveUnitAsCpp(const QString &inputFileName, const QString &outputFileName, QV4::CompiledData::CompilationUnit *unit, QString *errorString)
+{
+ QSaveFile f(outputFileName);
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ *errorString = f.errorString();
+ return false;
+ }
+
+ auto writeStr = [&f, errorString](const QByteArray &data) {
+ if (f.write(data) != data.size()) {
+ *errorString = f.errorString();
+ return false;
+ }
+ return true;
+ };
+
+ if (!writeStr("// "))
+ return false;
+
+ if (!writeStr(inputFileName.toUtf8()))
+ return false;
+
+ if (!writeStr("\n"))
+ return false;
+
+ if (!writeStr(QByteArrayLiteral("namespace QmlCacheGeneratedCode {\nnamespace ")))
+ return false;
+
+ if (!writeStr(symbolNamespaceForPath(inputFileName).toUtf8()))
+ return false;
+
+ if (!writeStr(QByteArrayLiteral(" {\nextern const unsigned char qmlData alignas(16) [] = {\n")))
+ return false;
+
+ QByteArray hexifiedData;
+ {
+ QByteArray modifiedUnit;
+ modifiedUnit.resize(unit->data->unitSize);
+ memcpy(modifiedUnit.data(), unit->data, unit->data->unitSize);
+ const char *dataPtr = modifiedUnit.data();
+ QV4::CompiledData::Unit *unitPtr;
+ memcpy(&unitPtr, &dataPtr, sizeof(unitPtr));
+ unitPtr->flags |= QV4::CompiledData::Unit::StaticData;
+
+ QTextStream stream(&hexifiedData);
+ const uchar *begin = reinterpret_cast<const uchar *>(modifiedUnit.constData());
+ const uchar *end = begin + unit->data->unitSize;
+ stream << hex;
+ int col = 0;
+ for (const uchar *data = begin; data < end; ++data, ++col) {
+ if (data > begin)
+ stream << ',';
+ if (col % 8 == 0) {
+ stream << '\n';
+ col = 0;
+ }
+ stream << "0x" << *data;
+ }
+ stream << '\n';
+ };
+
+ if (!writeStr(hexifiedData))
+ return false;
+
+ if (!writeStr("};\n}\n}\n"))
+ return false;
+
+ if (!f.commit()) {
+ *errorString = f.errorString();
+ return false;
+ }
+
+ return true;
+}
+
int main(int argc, char **argv)
{
// Produce reliably the same output for the same input by disabling QHash's random seeding.
@@ -322,11 +420,14 @@ int main(int argc, char **argv)
parser.addHelpOption();
parser.addVersionOption();
- QCommandLineOption targetArchitectureOption(QStringLiteral("target-architecture"), QCoreApplication::translate("main", "Target architecture"), QCoreApplication::translate("main", "architecture"));
- parser.addOption(targetArchitectureOption);
-
- QCommandLineOption targetABIOption(QStringLiteral("target-abi"), QCoreApplication::translate("main", "Target architecture binary interface"), QCoreApplication::translate("main", "abi"));
- parser.addOption(targetABIOption);
+ 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 outputFileOption(QStringLiteral("o"), QCoreApplication::translate("main", "Output file name"), QCoreApplication::translate("main", "file name"));
parser.addOption(outputFileOption);
@@ -337,51 +438,119 @@ int main(int argc, char **argv)
parser.addPositionalArgument(QStringLiteral("[qml file]"),
QStringLiteral("QML source file to generate cache for."));
+ parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
+
parser.process(app);
- if (!parser.isSet(targetArchitectureOption)) {
- fprintf(stderr, "Target architecture not specified. Please specify with --target-architecture=<arch>\n");
- parser.showHelp();
- return EXIT_FAILURE;
- }
+ enum Output {
+ GenerateCpp,
+ GenerateCacheFile,
+ GenerateLoader
+ } target = GenerateCacheFile;
-// if (parser.isSet(checkIfSupportedOption)) {
-// if (isel.isNull())
-// return EXIT_FAILURE;
-// else
-// return EXIT_SUCCESS;
-// }
+ 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;
+ }
const QStringList sources = parser.positionalArguments();
if (sources.isEmpty()){
parser.showHelp();
- } else if (sources.count() > 1) {
+ } else if (sources.count() > 1 && target != GenerateLoader) {
fprintf(stderr, "%s\n", qPrintable(QStringLiteral("Too many input files specified: '") + sources.join(QStringLiteral("' '")) + QLatin1Char('\'')));
return EXIT_FAILURE;
}
+
const QString inputFile = sources.first();
+ if (outputFileName.isEmpty())
+ outputFileName = inputFile + QLatin1Char('c');
- Error error;
+ if (parser.isSet(filterResourceFileOption)) {
+ return filterResourceFile(inputFile, outputFileName);
+ }
- QString outputFileName = inputFile + QLatin1Char('c');
- if (parser.isSet(outputFileOption))
- outputFileName = parser.value(outputFileOption);
+ if (target == GenerateLoader) {
+ ResourceFileMapper mapper(sources);
+
+ Error error;
+ if (!generateLoader(mapper.qmlCompilerFiles(), outputFileName, parser.values(resourceFileMappingOption), &error.message)) {
+ error.augment(QLatin1String("Error generating loader stub: ")).print();
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+ }
+
+ QString inputFileUrl = inputFile;
+
+ SaveFunction saveFunction;
+ if (target == GenerateCpp) {
+ ResourceFileMapper 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](QV4::CompiledData::CompilationUnit *unit, QString *errorString) {
+ return saveUnitAsCpp(inputResourcePath, outputFileName, unit, errorString);
+ };
+
+ } else {
+ saveFunction = [outputFileName](QV4::CompiledData::CompilationUnit *unit, QString *errorString) {
+ return unit->saveToDisk(outputFileName, errorString);
+ };
+ }
+
+ setupIllegalNames();
- const QString targetABI = parser.value(targetABIOption);
if (inputFile.endsWith(QLatin1String(".qml"))) {
- if (!compileQmlFile(inputFile, outputFileName, targetABI, &error)) {
+ Error error;
+ if (!compileQmlFile(inputFile, saveFunction, &error)) {
error.augment(QLatin1String("Error compiling qml file: ")).print();
return EXIT_FAILURE;
}
} else if (inputFile.endsWith(QLatin1String(".js"))) {
- if (!compileJSFile(inputFile, outputFileName, targetABI, &error)) {
- error.augment(QLatin1String("Error compiling qml file: ")).print();
+ Error error;
+ if (!compileJSFile(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)); }
-
+ fprintf(stderr, "Ignoring %s input file as it is not QML source code - maybe remove from QML_FILES?\n", qPrintable(inputFile));
+ }
return EXIT_SUCCESS;
}