summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSoheil Armin <[email protected]>2024-11-08 00:09:33 +0200
committerSoheil Armin <[email protected]>2025-01-17 16:02:52 +0000
commit68785b3e59c8a8f4a383051123b87af3d930ff18 (patch)
treea792f4a2b671544ce285154c71111f5610e6cb8d
parent7ed88eb565d40b195aa868e67777872ef07a5ea2 (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.cpp153
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;
}