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 Change-Id: Iebce6495d9d29af32c3f1f97274c252444d2864e Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io> (cherry picked from commit 68785b3e59c8a8f4a383051123b87af3d930ff18) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> (cherry picked from commit 8d97598e9c2a53a49b1fc053ab8e23bb8d16d8ee)
This commit is contained in:
parent
ad98ad04bf
commit
c5ac6e08fb
@ -3459,6 +3459,30 @@ bool writeDependencyFile(const Options &options)
|
|||||||
|
|
||||||
int generateJavaQmlComponents(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,
|
const auto getImportPaths = [options](const QString &buildPath, const QString &libName,
|
||||||
QStringList &appImports, QStringList &externalImports) -> bool {
|
QStringList &appImports, QStringList &externalImports) -> bool {
|
||||||
QFile confRspFile("%1/.qt/qml_imports/%2_conf.rsp"_L1.arg(buildPath, libName));
|
QFile confRspFile("%1/.qt/qml_imports/%2_conf.rsp"_L1.arg(buildPath, libName));
|
||||||
@ -3635,24 +3659,13 @@ int generateJavaQmlComponents(const Options &options)
|
|||||||
<< "import org.qtproject.qt.android.QtQuickViewContent;\n\n";
|
<< "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 auto beginComponentBlock = [](QTextStream &stream, const QString &libName,
|
||||||
const QString &moduleName, const QString &preferPath,
|
const QString &moduleName, const QString &preferPath,
|
||||||
const ComponentInfo &componentInfo, int indentWidth = 8) {
|
const ComponentInfo &componentInfo, int indentWidth = 8) {
|
||||||
const QString indent(indentWidth, u' ');
|
const QString indent(indentWidth, u' ');
|
||||||
|
|
||||||
stream << indent
|
stream << indent
|
||||||
<< "public final static class %1 extends QtQuickViewContent {\n"_L1
|
<< "public final class %1 extends QtQuickViewContent {\n"_L1
|
||||||
.arg(componentInfo.name)
|
.arg(componentInfo.name)
|
||||||
<< indent << " @Override public String getLibraryName() {\n"_L1
|
<< indent << " @Override public String getLibraryName() {\n"_L1
|
||||||
<< indent << " return \"%1\";\n"_L1.arg(libName)
|
<< indent << " return \"%1\";\n"_L1.arg(libName)
|
||||||
@ -3666,14 +3679,14 @@ int generateJavaQmlComponents(const Options &options)
|
|||||||
<< indent << " }\n"_L1;
|
<< indent << " }\n"_L1;
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto beginPropertyBlock = [](QTextStream &stream, const QJsonObject &propertyData,
|
const auto beginPropertyBlock = [firstCharToUpper](QTextStream &stream,
|
||||||
|
const QJsonObject &propertyData,
|
||||||
int indentWidth = 8) {
|
int indentWidth = 8) {
|
||||||
const QString indent(indentWidth, u' ');
|
const QString indent(indentWidth, u' ');
|
||||||
const QString propertyName = propertyData["name"_L1].toString();
|
const QString propertyName = propertyData["name"_L1].toString();
|
||||||
if (propertyName.isEmpty())
|
if (propertyName.isEmpty())
|
||||||
return;
|
return;
|
||||||
const QString upperPropertyName =
|
const QString upperPropertyName = firstCharToUpper(propertyName);
|
||||||
propertyName[0].toUpper() + propertyName.last(propertyName.size() - 1);
|
|
||||||
const QString typeName = propertyData["typeName"_L1].toString();
|
const QString typeName = propertyData["typeName"_L1].toString();
|
||||||
const bool isReadyonly = propertyData["isReadonly"_L1].toBool();
|
const bool isReadyonly = propertyData["isReadonly"_L1].toBool();
|
||||||
|
|
||||||
@ -3697,7 +3710,8 @@ int generateJavaQmlComponents(const Options &options)
|
|||||||
<< indent << "}\n";
|
<< indent << "}\n";
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto beginSignalBlock = [](QTextStream &stream, const QJsonObject &methodData,
|
const auto beginSignalBlock = [firstCharToUpper](QTextStream &stream,
|
||||||
|
const QJsonObject &methodData,
|
||||||
int indentWidth = 8) {
|
int indentWidth = 8) {
|
||||||
const QString indent(indentWidth, u' ');
|
const QString indent(indentWidth, u' ');
|
||||||
if (methodData["methodType"_L1] != 0)
|
if (methodData["methodType"_L1] != 0)
|
||||||
@ -3709,8 +3723,7 @@ int generateJavaQmlComponents(const Options &options)
|
|||||||
const QString methodName = methodData["name"_L1].toString();
|
const QString methodName = methodData["name"_L1].toString();
|
||||||
if (methodName.isEmpty())
|
if (methodName.isEmpty())
|
||||||
return;
|
return;
|
||||||
const QString upperMethodName =
|
const QString upperMethodName = firstCharToUpper(methodName);
|
||||||
methodName[0].toUpper() + methodName.last(methodName.size() - 1);
|
|
||||||
const QString typeName = !parameters.isEmpty()
|
const QString typeName = !parameters.isEmpty()
|
||||||
? parameters[0].toObject()["typeName"_L1].toString()
|
? parameters[0].toObject()["typeName"_L1].toString()
|
||||||
: "void"_L1;
|
: "void"_L1;
|
||||||
@ -3725,18 +3738,20 @@ int generateJavaQmlComponents(const Options &options)
|
|||||||
<< indent << "}\n";
|
<< indent << "}\n";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr static auto markerFileName = "qml_java_contents"_L1;
|
||||||
const QString libName(options.applicationBinary);
|
const QString libName(options.applicationBinary);
|
||||||
const QString libClassname = libName[0].toUpper() + libName.last(libName.size() - 1);
|
QString javaPackageBase = options.packageName;
|
||||||
const QString javaPackage = options.packageName;
|
const QString expectedBaseLeaf = ".%1"_L1.arg(libName);
|
||||||
const QString outputDir = "%1/src/%2"_L1.arg(options.outputDirectory,
|
if (!javaPackageBase.endsWith(expectedBaseLeaf))
|
||||||
QString(javaPackage).replace(u'.', u'/'));
|
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 buildPath(QDir(options.buildDirectory).absolutePath());
|
||||||
const QString domBinaryPath(options.qmlDomBinaryPath);
|
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));
|
fprintf(stdout, "Generating Java QML Components in %s directory.\n", qPrintable(baseSourceDir));
|
||||||
if (!QDir().current().mkpath(outputDir)) {
|
if (!QDir().current().mkpath(baseSourceDir)) {
|
||||||
fprintf(stderr, "Cannot create %s directory\n", qPrintable(outputDir));
|
fprintf(stderr, "Cannot create %s directory\n", qPrintable(baseSourceDir));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3745,20 +3760,12 @@ int generateJavaQmlComponents(const Options &options)
|
|||||||
if (!getImportPaths(buildPath, libName, appImports, externalImports))
|
if (!getImportPaths(buildPath, libName, appImports, externalImports))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
QTextStream outputStream;
|
// Remove previous directories generated by this code generator
|
||||||
QFile outputFile;
|
{
|
||||||
|
const QString srcDir = "%1/src"_L1.arg(options.outputDirectory);
|
||||||
if (!leafEqualsLibname) {
|
QDirIterator iter(srcDir, { markerFileName }, QDir::Files, QDirIterator::Subdirectories);
|
||||||
outputFile.setFileName("%1/%2.java"_L1.arg(outputDir, libClassname));
|
while (iter.hasNext())
|
||||||
if (outputFile.exists())
|
iter.nextFileInfo().dir().removeRecursively();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int generatedComponents = 0;
|
int generatedComponents = 0;
|
||||||
@ -3767,9 +3774,7 @@ int generateJavaQmlComponents(const Options &options)
|
|||||||
if (!moduleInfo.isValid())
|
if (!moduleInfo.isValid())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const QString moduleClassname = moduleInfo.moduleName[0].toUpper()
|
const QString modulePackageSuffix = upperFirstAndAfterDot(moduleInfo.moduleName);
|
||||||
+ moduleInfo.moduleName.last(moduleInfo.moduleName.size() - 1);
|
|
||||||
|
|
||||||
if (moduleInfo.moduleName == libName) {
|
if (moduleInfo.moduleName == libName) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"A QML module name (%s) cannot be the same as the target name when building "
|
"A QML module name (%s) cannot be the same as the target name when building "
|
||||||
@ -3778,30 +3783,24 @@ int generateJavaQmlComponents(const Options &options)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int indentBase = 4;
|
const QString javaPackage = "%1.%2"_L1.arg(javaPackageBase, modulePackageSuffix);
|
||||||
if (leafEqualsLibname) {
|
const QString outputDir =
|
||||||
indentBase = 0;
|
"%1/%2"_L1.arg(baseSourceDir, QString(modulePackageSuffix).replace(u'.', u'/'));
|
||||||
QIODevice *outputStreamDevice = outputStream.device();
|
if (!QDir().current().mkpath(outputDir)) {
|
||||||
if (outputStreamDevice) {
|
fprintf(stderr, "Cannot create %s directory\n", qPrintable(outputDir));
|
||||||
outputStream.flush();
|
|
||||||
outputStream.reset();
|
|
||||||
outputStreamDevice->close();
|
|
||||||
}
|
|
||||||
|
|
||||||
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()));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
outputStream.setDevice(&outputFile);
|
// Add a marker file to indicate this as a module package source directory
|
||||||
createHeaderBlock(outputStream, javaPackage);
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
beginModuleBlock(outputStream, moduleClassname, leafEqualsLibname, indentBase);
|
int indentBase = 0;
|
||||||
indentBase += 4;
|
|
||||||
|
|
||||||
for (const auto &qmlComponent : moduleInfo.qmlComponents) {
|
for (const auto &qmlComponent : moduleInfo.qmlComponents) {
|
||||||
const bool isSelected = options.selectedJavaQmlComponents.contains(
|
const bool isSelected = options.selectedJavaQmlComponents.contains(
|
||||||
@ -3815,6 +3814,11 @@ int generateJavaQmlComponents(const Options &options)
|
|||||||
if (component.isEmpty())
|
if (component.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
QByteArray componentClassBody;
|
||||||
|
QTextStream outputStream(&componentClassBody, QTextStream::ReadWrite);
|
||||||
|
|
||||||
|
createHeaderBlock(outputStream, javaPackage);
|
||||||
|
|
||||||
beginComponentBlock(outputStream, libName, moduleInfo.moduleName, moduleInfo.preferPath,
|
beginComponentBlock(outputStream, libName, moduleInfo.moduleName, moduleInfo.preferPath,
|
||||||
qmlComponent, indentBase);
|
qmlComponent, indentBase);
|
||||||
indentBase += 4;
|
indentBase += 4;
|
||||||
@ -3829,16 +3833,23 @@ int generateJavaQmlComponents(const Options &options)
|
|||||||
|
|
||||||
indentBase -= 4;
|
indentBase -= 4;
|
||||||
endBlock(outputStream, indentBase);
|
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++;
|
generatedComponents++;
|
||||||
}
|
}
|
||||||
indentBase -= 4;
|
|
||||||
endBlock(outputStream, indentBase);
|
|
||||||
}
|
}
|
||||||
if (!leafEqualsLibname)
|
|
||||||
endBlock(outputStream, 0);
|
|
||||||
|
|
||||||
outputStream.flush();
|
|
||||||
outputStream.device()->close();
|
|
||||||
return generatedComponents;
|
return generatedComponents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user