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 \externalpage https://source.android.com/devices/tech/debug/tagged-pointers
\title Android: 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 \externalpage https://developer.apple.com/documentation/uikit/uiapplication/1622952-canopenurl
\title iOS: 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" _qt_internal_add_android_deployment_property(file_contents "android-package-source-directory"
${target} "_qt_android_native_package_source_dir") ${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 # version code
_qt_internal_add_android_deployment_property(file_contents "android-version-code" _qt_internal_add_android_deployment_property(file_contents "android-version-code"
${target} "QT_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()} \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 \page cmake-target-property-qt-android-version-code.html
\ingroup cmake-properties-qtcore \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_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_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_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_NAME}{QT_ANDROID_VERSION_NAME}
\li \l{cmake-target-property-QT_ANDROID_VERSION_CODE}{QT_ANDROID_VERSION_CODE} \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} \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; return configs;
} }
QString cleanPackageName(QString packageName) QString cleanPackageName(QString packageName, bool *cleaned = nullptr)
{ {
auto isLegalChar = [] (QChar c) -> bool { auto isLegalChar = [] (QChar c) -> bool {
ushort ch = c.unicode(); ushort ch = c.unicode();
return (ch >= '0' && ch <= '9') || return (ch >= '0' && ch <= '9') ||
(ch >= 'A' && ch <= 'Z') || (ch >= 'A' && ch <= 'Z') ||
(ch >= 'a' && ch <= 'z') || (ch >= 'a' && ch <= 'z') ||
ch == '.'; ch == '.' || ch == '_';
}; };
if (cleaned)
*cleaned = false;
for (QChar &c : packageName) { for (QChar &c : packageName) {
if (!isLegalChar(c)) if (!isLegalChar(c)) {
c = u'_'; c = u'_';
if (cleaned)
*cleaned = true;
}
} }
static QStringList keywords; static QStringList keywords;
@ -848,12 +855,16 @@ QString cleanPackageName(QString packageName)
QChar c = word[0]; QChar c = word[0];
if ((c >= u'0' && c <= u'9') || c == u'_') { if ((c >= u'0' && c <= u'9') || c == u'_') {
packageName.insert(index + 1, u'a'); packageName.insert(index + 1, u'a');
if (cleaned)
*cleaned = true;
index = next + 1; index = next + 1;
continue; continue;
} }
} }
if (keywords.contains(word)) { if (keywords.contains(word)) {
packageName.insert(next, "_"_L1); packageName.insert(next, "_"_L1);
if (cleaned)
*cleaned = true;
index = next + 1; index = next + 1;
} else { } else {
index = next; index = next;
@ -885,26 +896,29 @@ QString detectLatestAndroidPlatform(const QString &sdkPath)
QString extractPackageName(Options *options) 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); QFile androidManifestXml(options->androidSourceDirectory + "/AndroidManifest.xml"_L1);
if (androidManifestXml.open(QIODevice::ReadOnly)) { if (androidManifestXml.open(QIODevice::ReadOnly)) {
QXmlStreamReader reader(&androidManifestXml); QXmlStreamReader reader(&androidManifestXml);
while (!reader.atEnd()) { while (!reader.atEnd()) {
reader.readNext(); reader.readNext();
if (reader.isStartElement() && reader.name() == "manifest"_L1) if (reader.isStartElement() && reader.name() == "manifest"_L1) {
packageName = reader.attributes().value("package"_L1).toString(); QString packageName = reader.attributes().value("package"_L1).toString();
if (!packageName.isEmpty() && packageName != "org.qtproject.example"_L1)
return packageName;
break;
}
} }
} }
if (packageName.isEmpty()) { return QString();
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);
} }
bool parseCmakeBoolean(const QJsonValue &value) 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; using ItFlag = QDirListing::IteratorFlag;
const QJsonValue deploymentDependencies = jsonObject.value("deployment-dependencies"_L1); const QJsonValue deploymentDependencies = jsonObject.value("deployment-dependencies"_L1);
@ -1358,7 +1390,6 @@ bool readInputFile(Options *options)
options->isZstdCompressionEnabled = zstdCompressionFlag.toBool(); options->isZstdCompressionEnabled = zstdCompressionFlag.toBool();
} }
} }
options->packageName = extractPackageName(options);
return true; return true;
} }

View File

@ -30,6 +30,7 @@ set_target_properties(${target} PROPERTIES
QT_ANDROID_SDK_BUILD_TOOLS_REVISION "23.0.2" QT_ANDROID_SDK_BUILD_TOOLS_REVISION "23.0.2"
QT_ANDROID_MIN_SDK_VERSION "1" QT_ANDROID_MIN_SDK_VERSION "1"
QT_ANDROID_TARGET_SDK_VERSION "2" 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_DEPENDENCIES "dep1.so;dep2.so;dep3.so"
QT_ANDROID_DEPLOYMENT_SETTINGS_FILE "attempt_to_rewrite.json" QT_ANDROID_DEPLOYMENT_SETTINGS_FILE "attempt_to_rewrite.json"
QT_ANDROID_EXTRA_LIBS QT_ANDROID_EXTRA_LIBS
@ -53,6 +54,7 @@ set_target_properties(${target} PROPERTIES
QT_ANDROID_SDK_BUILD_TOOLS_REVISION "23.0.2" QT_ANDROID_SDK_BUILD_TOOLS_REVISION "23.0.2"
QT_ANDROID_MIN_SDK_VERSION "1" QT_ANDROID_MIN_SDK_VERSION "1"
QT_ANDROID_TARGET_SDK_VERSION "2" 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_DEPENDENCIES "dep1.so;dep2.so;dep3.so"
QT_ANDROID_EXTRA_LIBS QT_ANDROID_EXTRA_LIBS
"some/path/to/lib1.so;some/path\\to/lib2.so;some\\path\\to\\lib3.so;some/path/to/lib4.so" "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"; << "1";
QTest::newRow("android-target-sdk-version") << "android-target-sdk-version" QTest::newRow("android-target-sdk-version") << "android-target-sdk-version"
<< "2"; << "2";
QTest::newRow("android-package-name") << "android-package-name"
<< "org.qtproject.android_deployment_settings_test";
} }
void tst_android_deployment_settings::DeploymentSettings() void tst_android_deployment_settings::DeploymentSettings()