diff options
author | Soheil Armin <[email protected]> | 2024-11-08 00:09:33 +0200 |
---|---|---|
committer | Soheil Armin <[email protected]> | 2025-01-17 16:02:52 +0000 |
commit | 68785b3e59c8a8f4a383051123b87af3d930ff18 (patch) | |
tree | a792f4a2b671544ce285154c71111f5610e6cb8d | |
parent | 7ed88eb565d40b195aa868e67777872ef07a5ea2 (diff) |
Android: Handle dotted URI QML Modules in Java code gen
A QML module can have URI that include dot character.
In this change, we refactor the code generator to create
the same hierarchies as a Java package structure. Each
QML module URI will be appended to the base application
package name to form a package that represents the QML
module as a Java package. Then the generated code of each
QML component will be placed in a single file.
This change, also refactor how to generated code should
be written in the file, by buffering the generated code
to a QByteArray first, and then flushing it into the
target file. We also create a marker file inside the
directories of each module, so that we can entirely
remove the directory and all its files before generating
new code during the next build.
Fixes: QTBUG-125891
Fixes: QTBUG-125970
Fixes: QTBUG-125971
Pick-to: 6.8 6.9
Change-Id: Iebce6495d9d29af32c3f1f97274c252444d2864e
Reviewed-by: Assam Boudjelthia <[email protected]>
-rw-r--r-- | src/tools/androiddeployqt/main.cpp | 153 |
1 files changed, 82 insertions, 71 deletions
diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp index 1892e3d79ef..98f0b40062c 100644 --- a/src/tools/androiddeployqt/main.cpp +++ b/src/tools/androiddeployqt/main.cpp @@ -3508,6 +3508,30 @@ bool writeDependencyFile(const Options &options) int generateJavaQmlComponents(const Options &options) { + const auto firstCharToUpper = [](const QString &str) -> QString { + if (str.isEmpty()) + return str; + return str.left(1).toUpper() + str.mid(1); + }; + + const auto upperFirstAndAfterDot = [](QString str) -> QString { + if (str.isEmpty()) + return str; + + str[0] = str[0].toUpper(); + + for (int i = 0; i < str.size(); ++i) { + if (str[i] == "."_L1) { + // Move to the next character after the dot + int j = i + 1; + if (j < str.size()) { + str[j] = str[j].toUpper(); + } + } + } + return str; + }; + const auto getImportPaths = [options](const QString &buildPath, const QString &libName, QStringList &appImports, QStringList &externalImports) -> bool { QFile confRspFile("%1/.qt/qml_imports/%2_conf.rsp"_L1.arg(buildPath, libName)); @@ -3684,24 +3708,13 @@ int generateJavaQmlComponents(const Options &options) << "import org.qtproject.qt.android.QtQuickViewContent;\n\n"; }; - const auto beginLibraryBlock = [](QTextStream &stream, const QString &libName) { - stream << QLatin1StringView("public final class %1 {\n").arg(libName); - }; - - const auto beginModuleBlock = [](QTextStream &stream, const QString &moduleName, - bool topLevel = false, int indentWidth = 4) { - const QString indent(indentWidth, u' '); - stream << indent - << "public final%1 class %2 {\n"_L1.arg(topLevel ? ""_L1 : " static"_L1, moduleName); - }; - const auto beginComponentBlock = [](QTextStream &stream, const QString &libName, const QString &moduleName, const QString &preferPath, const ComponentInfo &componentInfo, int indentWidth = 8) { const QString indent(indentWidth, u' '); stream << indent - << "public final static class %1 extends QtQuickViewContent {\n"_L1 + << "public final class %1 extends QtQuickViewContent {\n"_L1 .arg(componentInfo.name) << indent << " @Override public String getLibraryName() {\n"_L1 << indent << " return \"%1\";\n"_L1.arg(libName) @@ -3715,14 +3728,14 @@ int generateJavaQmlComponents(const Options &options) << indent << " }\n"_L1; }; - const auto beginPropertyBlock = [](QTextStream &stream, const QJsonObject &propertyData, - int indentWidth = 8) { + const auto beginPropertyBlock = [firstCharToUpper](QTextStream &stream, + const QJsonObject &propertyData, + int indentWidth = 8) { const QString indent(indentWidth, u' '); const QString propertyName = propertyData["name"_L1].toString(); if (propertyName.isEmpty()) return; - const QString upperPropertyName = - propertyName[0].toUpper() + propertyName.last(propertyName.size() - 1); + const QString upperPropertyName = firstCharToUpper(propertyName); const QString typeName = propertyData["typeName"_L1].toString(); const bool isReadyonly = propertyData["isReadonly"_L1].toBool(); @@ -3746,8 +3759,9 @@ int generateJavaQmlComponents(const Options &options) << indent << "}\n"; }; - const auto beginSignalBlock = [](QTextStream &stream, const QJsonObject &methodData, - int indentWidth = 8) { + const auto beginSignalBlock = [firstCharToUpper](QTextStream &stream, + const QJsonObject &methodData, + int indentWidth = 8) { const QString indent(indentWidth, u' '); if (methodData["methodType"_L1] != 0) return; @@ -3758,8 +3772,7 @@ int generateJavaQmlComponents(const Options &options) const QString methodName = methodData["name"_L1].toString(); if (methodName.isEmpty()) return; - const QString upperMethodName = - methodName[0].toUpper() + methodName.last(methodName.size() - 1); + const QString upperMethodName = firstCharToUpper(methodName); const QString typeName = !parameters.isEmpty() ? parameters[0].toObject()["typeName"_L1].toString() : "void"_L1; @@ -3774,18 +3787,20 @@ int generateJavaQmlComponents(const Options &options) << indent << "}\n"; }; + constexpr static auto markerFileName = "qml_java_contents"_L1; const QString libName(options.applicationBinary); - const QString libClassname = libName[0].toUpper() + libName.last(libName.size() - 1); - const QString javaPackage = options.packageName; - const QString outputDir = "%1/src/%2"_L1.arg(options.outputDirectory, - QString(javaPackage).replace(u'.', u'/')); + QString javaPackageBase = options.packageName; + const QString expectedBaseLeaf = ".%1"_L1.arg(libName); + if (!javaPackageBase.endsWith(expectedBaseLeaf)) + javaPackageBase += expectedBaseLeaf; + const QString baseSourceDir = "%1/src/%2"_L1.arg(options.outputDirectory, + QString(javaPackageBase).replace(u'.', u'/')); const QString buildPath(QDir(options.buildDirectory).absolutePath()); const QString domBinaryPath(options.qmlDomBinaryPath); - const bool leafEqualsLibname = javaPackage.endsWith(".%1"_L1.arg(libName)); - fprintf(stdout, "Generating Java QML Components in %s directory.\n", qPrintable(outputDir)); - if (!QDir().current().mkpath(outputDir)) { - fprintf(stderr, "Cannot create %s directory\n", qPrintable(outputDir)); + fprintf(stdout, "Generating Java QML Components in %s directory.\n", qPrintable(baseSourceDir)); + if (!QDir().current().mkpath(baseSourceDir)) { + fprintf(stderr, "Cannot create %s directory\n", qPrintable(baseSourceDir)); return false; } @@ -3794,20 +3809,12 @@ int generateJavaQmlComponents(const Options &options) if (!getImportPaths(buildPath, libName, appImports, externalImports)) return false; - QTextStream outputStream; - QFile outputFile; - - if (!leafEqualsLibname) { - outputFile.setFileName("%1/%2.java"_L1.arg(outputDir, libClassname)); - if (outputFile.exists()) - outputFile.remove(); - if (!outputFile.open(QFile::WriteOnly)) { - fprintf(stderr, "Cannot open %s file to write.\n", qPrintable(outputFile.fileName())); - return false; - } - outputStream.setDevice(&outputFile); - createHeaderBlock(outputStream, javaPackage); - beginLibraryBlock(outputStream, libClassname); + // Remove previous directories generated by this code generator + { + const QString srcDir = "%1/src"_L1.arg(options.outputDirectory); + QDirIterator iter(srcDir, { markerFileName }, QDir::Files, QDirIterator::Subdirectories); + while (iter.hasNext()) + iter.nextFileInfo().dir().removeRecursively(); } int generatedComponents = 0; @@ -3816,9 +3823,7 @@ int generateJavaQmlComponents(const Options &options) if (!moduleInfo.isValid()) continue; - const QString moduleClassname = moduleInfo.moduleName[0].toUpper() - + moduleInfo.moduleName.last(moduleInfo.moduleName.size() - 1); - + const QString modulePackageSuffix = upperFirstAndAfterDot(moduleInfo.moduleName); if (moduleInfo.moduleName == libName) { fprintf(stderr, "A QML module name (%s) cannot be the same as the target name when building " @@ -3827,30 +3832,24 @@ int generateJavaQmlComponents(const Options &options) return false; } - int indentBase = 4; - if (leafEqualsLibname) { - indentBase = 0; - QIODevice *outputStreamDevice = outputStream.device(); - if (outputStreamDevice) { - outputStream.flush(); - outputStream.reset(); - outputStreamDevice->close(); - } + const QString javaPackage = "%1.%2"_L1.arg(javaPackageBase, modulePackageSuffix); + const QString outputDir = + "%1/%2"_L1.arg(baseSourceDir, QString(modulePackageSuffix).replace(u'.', u'/')); + if (!QDir().current().mkpath(outputDir)) { + fprintf(stderr, "Cannot create %s directory\n", qPrintable(outputDir)); + return false; + } - outputFile.setFileName("%1/%2.java"_L1.arg(outputDir,moduleClassname)); - if (outputFile.exists() && !outputFile.remove()) - return false; - if (!outputFile.open(QFile::WriteOnly)) { - fprintf(stderr, "Cannot open %s file to write.\n", qPrintable(outputFile.fileName())); + // Add a marker file to indicate this as a module package source directory + { + QFile markerFile("%1/%2"_L1.arg(outputDir, markerFileName)); + if (!markerFile.open(QFile::WriteOnly)) { + fprintf(stderr, "Cannot create %s file\n", qPrintable(markerFile.fileName())); return false; } - - outputStream.setDevice(&outputFile); - createHeaderBlock(outputStream, javaPackage); } - beginModuleBlock(outputStream, moduleClassname, leafEqualsLibname, indentBase); - indentBase += 4; + int indentBase = 0; for (const auto &qmlComponent : moduleInfo.qmlComponents) { const bool isSelected = options.selectedJavaQmlComponents.contains( @@ -3864,6 +3863,11 @@ int generateJavaQmlComponents(const Options &options) if (component.isEmpty()) continue; + QByteArray componentClassBody; + QTextStream outputStream(&componentClassBody, QTextStream::ReadWrite); + + createHeaderBlock(outputStream, javaPackage); + beginComponentBlock(outputStream, libName, moduleInfo.moduleName, moduleInfo.preferPath, qmlComponent, indentBase); indentBase += 4; @@ -3878,16 +3882,23 @@ int generateJavaQmlComponents(const Options &options) indentBase -= 4; endBlock(outputStream, indentBase); + outputStream.flush(); + + // Write component class body to file + QFile outputFile("%1/%2.java"_L1.arg(outputDir, qmlComponent.name)); + if (outputFile.exists()) + outputFile.remove(); + if (!outputFile.open(QFile::WriteOnly)) { + fprintf(stderr, "Cannot open %s file to write.\n", + qPrintable(outputFile.fileName())); + return false; + } + outputFile.write(componentClassBody); + outputFile.close(); + generatedComponents++; } - indentBase -= 4; - endBlock(outputStream, indentBase); } - if (!leafEqualsLibname) - endBlock(outputStream, 0); - - outputStream.flush(); - outputStream.device()->close(); return generatedComponents; } |