Android:CMake: Add QT_ANDROID_PACKAGE_NAME property

Allow setting the package name directly from CMake properties.
If the package name is not set manually under AndroidManifest.xml
nor build.gradle, then the value set by this property is used.
The value is passed to build.gradle as "namespace" which is the
way to set the package name after AGP 7.4 instead of
AndroidManifest.xml "package" attribute.

Task-number: QTBUG-106907
Change-Id: I94bd73c294d751eabfd96c0a10a6b3ff270d8137
Reviewed-by: Tinja Paavoseppä <tinja.paavoseppa@qt.io>
This commit is contained in:
Assam Boudjelthia 2024-05-10 14:45:50 +03:00
parent c9ddc4b8e1
commit fbb35cdb0b
7 changed files with 109 additions and 16 deletions

View File

@ -32,6 +32,10 @@
\externalpage https://source.android.com/devices/tech/debug/tagged-pointers
\title Android: Tagged Pointers
*/
/*!
\externalpage https://developer.android.com/build/configure-app-module
\title Android: Configure the app module
*/
/*!
\externalpage https://developer.apple.com/documentation/uikit/uiapplication/1622952-canopenurl
\title iOS: canOpenURL:

View File

@ -216,6 +216,10 @@ function(qt6_android_generate_deployment_settings target)
_qt_internal_add_android_deployment_property(file_contents "android-package-source-directory"
${target} "_qt_android_native_package_source_dir")
# version code
_qt_internal_add_android_deployment_property(file_contents "android-package-name"
${target} "QT_ANDROID_PACKAGE_NAME")
# version code
_qt_internal_add_android_deployment_property(file_contents "android-version-code"
${target} "QT_ANDROID_VERSION_CODE")

View File

@ -244,6 +244,55 @@ CMake will attempt to use the latest installed version.
\sa{qt6_android_generate_deployment_settings}{qt_android_generate_deployment_settings()}
*/
/*!
\page cmake-target-property-qt-android-package-name.html
\ingroup cmake-properties-qtcore
\ingroup cmake-target-properties-qtcore
\title QT_ANDROID_PACKAGE_NAME
\target cmake-target-property-QT_ANDROID_PACKAGE_NAME
\summary {The app's package name.}
\cmakepropertysince 6.8
\preliminarycmakeproperty
\cmakepropertyandroidonly
Specifies the app's package name. This is usually a unique dot separated
name for the app, that will be used to identify the app on devices or in
the Play Store. For example, "org.qtproject.example.gallery".
The package name set by this property is passed to the \c build.gradle file
as a \c namespace property, instead of \c AndroidManifest.xml, since the
latter is deprecated since Android Gradle Plugin 7.4.
The package name considers some words or characters as illegal and the build
will clean such names if any is encountered. An underscore (\c _) either replaces
illegal characters or is appended to illegal words.
\list
\li Allowed characters: alphanumeric, an underscore or a dot [a-zA-Z0-9_.].
\li Illegal words: abstract, continue, for, new, switch, assert, default,
if, package, synchronized, boolean, do, goto, private, this, break,
double, implements, protected, throw, byte, else, import, public,
throws, case, enum, instanceof, return, transient, catch, extends,
int, short, try, char, final, interface, static, void, class, finally,
long, strictfp, volatile, const, float, native, super, while.
\endlist
The default package name for Qt for Android apps is \c org.qtproject.example.<target_name>.
\note Setting the package name manually in \c build.gradle (via
\c namespace property) takes precedence over \c AndroidManifest.xml
(via \c package attribute), and the latter also takes precedence over
this property.
For more information, see Android's
\l{Android: Configure the app module}{configure the app module}.
\sa{qt6_android_generate_deployment_settings}{qt_android_generate_deployment_settings()}
*/
/*!
\page cmake-target-property-qt-android-version-code.html
\ingroup cmake-properties-qtcore

View File

@ -63,6 +63,7 @@ how to accomplish this.
\li \l{cmake-target-property-QT_ANDROID_MIN_SDK_VERSION}{QT_ANDROID_MIN_SDK_VERSION}
\li \l{cmake-target-property-QT_ANDROID_PACKAGE_SOURCE_DIR}{QT_ANDROID_PACKAGE_SOURCE_DIR}
\li \l{cmake-target-property-QT_ANDROID_TARGET_SDK_VERSION}{QT_ANDROID_TARGET_SDK_VERSION}
\li \l{cmake-target-property-QT_ANDROID_PACKAGE_NAME}{QT_ANDROID_PACKAGE_NAME}
\li \l{cmake-target-property-QT_ANDROID_VERSION_NAME}{QT_ANDROID_VERSION_NAME}
\li \l{cmake-target-property-QT_ANDROID_VERSION_CODE}{QT_ANDROID_VERSION_CODE}
\li \l{cmake-target-property-QT_QML_IMPORT_PATH}{QT_QML_IMPORT_PATH}

View File

@ -802,18 +802,25 @@ GradleBuildConfigs gradleBuildConfigs(const QString &path)
return configs;
}
QString cleanPackageName(QString packageName)
QString cleanPackageName(QString packageName, bool *cleaned = nullptr)
{
auto isLegalChar = [] (QChar c) -> bool {
ushort ch = c.unicode();
return (ch >= '0' && ch <= '9') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= 'a' && ch <= 'z') ||
ch == '.';
ch == '.' || ch == '_';
};
if (cleaned)
*cleaned = false;
for (QChar &c : packageName) {
if (!isLegalChar(c))
if (!isLegalChar(c)) {
c = u'_';
if (cleaned)
*cleaned = true;
}
}
static QStringList keywords;
@ -848,12 +855,16 @@ QString cleanPackageName(QString packageName)
QChar c = word[0];
if ((c >= u'0' && c <= u'9') || c == u'_') {
packageName.insert(index + 1, u'a');
if (cleaned)
*cleaned = true;
index = next + 1;
continue;
}
}
if (keywords.contains(word)) {
packageName.insert(next, "_"_L1);
if (cleaned)
*cleaned = true;
index = next + 1;
} else {
index = next;
@ -885,26 +896,29 @@ QString detectLatestAndroidPlatform(const QString &sdkPath)
QString extractPackageName(Options *options)
{
QString packageName;
{
const QString gradleBuildFile = options->androidSourceDirectory + "/build.gradle"_L1;
QString packageName = gradleBuildConfigs(gradleBuildFile).appNamespace;
if (!packageName.isEmpty() && packageName != "androidPackageName"_L1)
return packageName;
}
QFile androidManifestXml(options->androidSourceDirectory + "/AndroidManifest.xml"_L1);
if (androidManifestXml.open(QIODevice::ReadOnly)) {
QXmlStreamReader reader(&androidManifestXml);
while (!reader.atEnd()) {
reader.readNext();
if (reader.isStartElement() && reader.name() == "manifest"_L1)
packageName = reader.attributes().value("package"_L1).toString();
if (reader.isStartElement() && reader.name() == "manifest"_L1) {
QString packageName = reader.attributes().value("package"_L1).toString();
if (!packageName.isEmpty() && packageName != "org.qtproject.example"_L1)
return packageName;
break;
}
}
}
if (packageName.isEmpty()) {
const QString gradleBuildFile = options->androidSourceDirectory + "/build.gradle"_L1;
packageName = gradleBuildConfigs(gradleBuildFile).appNamespace;
}
if (packageName.isEmpty() || packageName == "androidPackageName"_L1)
packageName = "org.qtproject.example.%1"_L1.arg(options->applicationBinary);
return cleanPackageName(packageName);
return QString();
}
bool parseCmakeBoolean(const QJsonValue &value)
@ -1301,6 +1315,24 @@ bool readInputFile(Options *options)
}
}
{
const QJsonValue androidPackageName = jsonObject.value("android-package-name"_L1);
const QString extractedPackageName = extractPackageName(options);
if (!extractedPackageName.isEmpty())
options->packageName = extractedPackageName;
else if (!androidPackageName.isUndefined())
options->packageName = androidPackageName.toString();
else
options->packageName = "org.qtproject.example.%1"_L1.arg(options->applicationBinary);
bool cleaned;
options->packageName = cleanPackageName(options->packageName, &cleaned);
if (cleaned) {
fprintf(stderr, "Warning: Package name contained illegal characters and was cleaned "
"to \"%s\"\n", qPrintable(options->packageName));
}
}
{
using ItFlag = QDirListing::IteratorFlag;
const QJsonValue deploymentDependencies = jsonObject.value("deployment-dependencies"_L1);
@ -1358,7 +1390,6 @@ bool readInputFile(Options *options)
options->isZstdCompressionEnabled = zstdCompressionFlag.toBool();
}
}
options->packageName = extractPackageName(options);
return true;
}

View File

@ -30,6 +30,7 @@ set_target_properties(${target} PROPERTIES
QT_ANDROID_SDK_BUILD_TOOLS_REVISION "23.0.2"
QT_ANDROID_MIN_SDK_VERSION "1"
QT_ANDROID_TARGET_SDK_VERSION "2"
QT_ANDROID_PACKAGE_NAME "org.qtproject.android_deployment_settings_test"
QT_ANDROID_DEPLOYMENT_DEPENDENCIES "dep1.so;dep2.so;dep3.so"
QT_ANDROID_DEPLOYMENT_SETTINGS_FILE "attempt_to_rewrite.json"
QT_ANDROID_EXTRA_LIBS
@ -53,6 +54,7 @@ set_target_properties(${target} PROPERTIES
QT_ANDROID_SDK_BUILD_TOOLS_REVISION "23.0.2"
QT_ANDROID_MIN_SDK_VERSION "1"
QT_ANDROID_TARGET_SDK_VERSION "2"
QT_ANDROID_PACKAGE_NAME "org.qtproject.android_deployment_settings_test"
QT_ANDROID_DEPLOYMENT_DEPENDENCIES "dep1.so;dep2.so;dep3.so"
QT_ANDROID_EXTRA_LIBS
"some/path/to/lib1.so;some/path\\to/lib2.so;some\\path\\to\\lib3.so;some/path/to/lib4.so"

View File

@ -76,6 +76,8 @@ void tst_android_deployment_settings::DeploymentSettings_data()
<< "1";
QTest::newRow("android-target-sdk-version") << "android-target-sdk-version"
<< "2";
QTest::newRow("android-package-name") << "android-package-name"
<< "org.qtproject.android_deployment_settings_test";
}
void tst_android_deployment_settings::DeploymentSettings()