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)
|
||||
{
|
||||
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));
|
||||
@ -3635,24 +3659,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)
|
||||
@ -3666,14 +3679,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();
|
||||
|
||||
@ -3697,8 +3710,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;
|
||||
@ -3709,8 +3723,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;
|
||||
@ -3725,18 +3738,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;
|
||||
}
|
||||
|
||||
@ -3745,20 +3760,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;
|
||||
@ -3767,9 +3774,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 "
|
||||
@ -3778,30 +3783,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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
outputStream.setDevice(&outputFile);
|
||||
createHeaderBlock(outputStream, javaPackage);
|
||||
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;
|
||||
}
|
||||
|
||||
beginModuleBlock(outputStream, moduleClassname, leafEqualsLibname, indentBase);
|
||||
indentBase += 4;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
int indentBase = 0;
|
||||
|
||||
for (const auto &qmlComponent : moduleInfo.qmlComponents) {
|
||||
const bool isSelected = options.selectedJavaQmlComponents.contains(
|
||||
@ -3815,6 +3814,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;
|
||||
@ -3829,16 +3833,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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user