[PATCH v3 0/4] MR10354: wineandroid: update Android build to work with modern toolchains
Update the Android project and build logic to work with recent Gradle and Android SDK versions, adjust types for consistency, separate APK generation from the default build, and add the required staging steps to prepare assets and libraries before building the APK. These changes allow Wine to be built with NDK r29 and modern Android build tools. The resulting build runs on Android 7 and later (only devices using the legacy gralloc interface). The build process still remains somewhat non-standard, so a build script and screenshot are provided for reference. <details> <summary>Screenshot</summary> {width=792 height=600} </details> [wineandroid-builder.tar.gz](/uploads/0b47ae812cfcd2d64f71aa9dee7042f6/wineandroid-builder.tar.gz) -- v3: wineandroid: use local Gradle and Android SDK for APK build wineandroid: fix APK output path for recent Gradle https://gitlab.winehq.org/wine/wine/-/merge_requests/10354
From: Twaik Yont <9674930+twaik@users.noreply.github.com> The wineandroid Gradle project no longer builds with recent Android build tools. Replace jcenter() with google() and mavenCentral(), update the Android Gradle plugin, and add the required namespace and android:exported attributes. The archivesBaseName property is no longer supported and is replaced with the current mechanism for setting the APK output name. Update task wiring to use configureEach so icon generation and asset checksums continue to run correctly with the modern task graph. This allows wineandroid to build with recent Gradle and Android SDK versions. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/wineandroid.drv/AndroidManifest.xml | 1 + dlls/wineandroid.drv/build.gradle.in | 81 +++++++++++++++++------- 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/dlls/wineandroid.drv/AndroidManifest.xml b/dlls/wineandroid.drv/AndroidManifest.xml index 574b98e3820..7f0a5e6ce64 100644 --- a/dlls/wineandroid.drv/AndroidManifest.xml +++ b/dlls/wineandroid.drv/AndroidManifest.xml @@ -9,6 +9,7 @@ android:icon="@drawable/wine" android:label="Wine" > <activity + android:exported="true" android:label="Wine" android:name=".WineActivity" android:launchMode="singleInstance" diff --git a/dlls/wineandroid.drv/build.gradle.in b/dlls/wineandroid.drv/build.gradle.in index 662e8b7ea22..8d0e138c9a2 100644 --- a/dlls/wineandroid.drv/build.gradle.in +++ b/dlls/wineandroid.drv/build.gradle.in @@ -18,20 +18,31 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +if (GradleVersion.current() < GradleVersion.version("9.4.0")) { + throw new GradleException("Gradle 9.4.0+ required") +} + apply plugin: 'com.android.application' buildscript { repositories { - jcenter() + google() + mavenCentral() } dependencies { - classpath "com.android.tools.build:gradle:2.2.1" + classpath "com.android.tools.build:gradle:9.1.0" } } +repositories +{ + google() + mavenCentral() +} + def get_srcdir() { if (srcdir.equals(".")) { return "."; } @@ -52,32 +63,39 @@ def add_icon_task( dir, scale ) } } -def checksum_task() +def checksum_task = tasks.create( "checksumAssets", Exec ) { - return tasks.create( "checksumAssets", Exec ) { - commandLine "sh", "-c", - "(test -d assets && " + - "rm -f assets/files.sum assets/sums.sum && " + - "sha256sum \$(find assets -type f -print) | sed 's/ assets\\// /' >files.sum && " + - "sha256sum files.sum >sums.sum && " + - "mv files.sum sums.sum assets) || rm -rf assets"; - } + commandLine "sh", "-c", + "(test -d assets && " + + "rm -f assets/files.sum assets/sums.sum && " + + "sha256sum \$(find assets -type f -print) | sed 's/ assets\\// /' >files.sum && " + + "sha256sum files.sum >sums.sum && " + + "mv files.sum sums.sum assets) || rm -rf assets"; } -tasks.whenTaskAdded +def icon_tasks = [ + add_icon_task( "ldpi", 0.75 ), + add_icon_task( "mdpi", 1 ), + add_icon_task( "hdpi", 1.5 ), + add_icon_task( "xhdpi", 2 ), + add_icon_task( "xxhdpi", 3 ), + add_icon_task( "xxxhdpi", 4 ) +] + +tasks.configureEach { - if (name.equals( "generateDebugResources" )) + if (name.equals( "preBuild" ) || + name.equals( "processDebugResources" ) || + name.equals( "processDebugNavigationResources" ) || + name.equals( "mergeDebugResources" )) { - dependsOn add_icon_task( "ldpi", 0.75 ) - dependsOn add_icon_task( "mdpi", 1 ) - dependsOn add_icon_task( "hdpi", 1.5 ) - dependsOn add_icon_task( "xhdpi", 2 ) - dependsOn add_icon_task( "xxhdpi", 3 ) - dependsOn add_icon_task( "xxxhdpi", 4 ) + dependsOn icon_tasks } - if (name.equals( "generateDebugAssets" )) + if (name.equals( "preBuild" ) || + name.equals( "generateDebugAssets" ) || + name.equals( "mergeDebugAssets" )) { - dependsOn checksum_task() + dependsOn checksum_task } } @@ -88,8 +106,8 @@ tasks.withType(JavaCompile) android { - compileSdkVersion 25 - buildToolsVersion "25.0.3" + namespace "org.winehq.wine" + compileSdkVersion 36 defaultConfig { @@ -97,7 +115,6 @@ android minSdkVersion 17 versionCode 1 versionName "@PACKAGE_VERSION@" - setProperty( "archivesBaseName", "wine" ) } sourceSets @@ -110,3 +127,19 @@ android main.manifest.srcFile get_srcdir() + "/AndroidManifest.xml" } } + +configurations.configureEach +{ + exclude group: "org.jetbrains.kotlin", module: "kotlin-stdlib" +} + +androidComponents +{ + onVariants(selector().all()) + { variant -> + variant.outputs.forEach + { output -> + output.outputFileName.set("wine-${variant.name}.apk") + } + } +} -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10354
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Replace the bool type with BOOL for the is_desktop field to match the rest of device.c and avoid mixing C99 bool with Win32 types. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/wineandroid.drv/device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/wineandroid.drv/device.c b/dlls/wineandroid.drv/device.c index b976968f092..8ae0172a57f 100644 --- a/dlls/wineandroid.drv/device.c +++ b/dlls/wineandroid.drv/device.c @@ -132,7 +132,7 @@ struct ioctl_android_create_window struct ioctl_header hdr; int parent; float scale; - bool is_desktop; + BOOL is_desktop; }; struct ioctl_android_destroy_window -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10354
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Adjust the APK output path to build/outputs/apk/debug/ to match recent Gradle/AGP versions. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- configure | 2 +- configure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure b/configure index ac58d7c1874..bf8ee741b70 100755 --- a/configure +++ b/configure @@ -24686,7 +24686,7 @@ programs/winetest/build.rc: dummy @build=\"STRINGTABLE { 1 \\\"\`GIT_DIR=${wine_srcdir}.git git rev-parse HEAD 2>/dev/null\`\\\" }\" && (echo \$\$build | cmp -s - \$@) || echo \$\$build >\$@ || (rm -f \$@ && exit 1) dlls/wineandroid.drv/wine-debug.apk: dlls/wineandroid.drv/build.gradle ${wine_srcdir}dlls/wineandroid.drv/AndroidManifest.xml ${wine_srcdir}dlls/wineandroid.drv/WineActivity.java ${wine_srcdir}dlls/wineandroid.drv/wine.svg cd dlls/wineandroid.drv && gradle -q -Psrcdir=$srcdir assembleDebug - mv dlls/wineandroid.drv/build/outputs/apk/wine-debug.apk \$@" + mv dlls/wineandroid.drv/build/outputs/apk/debug/wine-debug.apk \$@" EXTERNAL_SUBDIRS="libs/capstone libs/c++ libs/c++abi libs/faudio libs/fluidsynth libs/gsm libs/icucommon libs/icui18n libs/jpeg libs/jxr libs/lcms2 libs/ldap libs/mpg123 libs/musl libs/png libs/tiff libs/tomcrypt libs/unwind libs/vkd3d libs/xml2 libs/xslt libs/zlib libs/compiler-rt" diff --git a/configure.ac b/configure.ac index 89243b45be0..7e801fa8af7 100644 --- a/configure.ac +++ b/configure.ac @@ -3838,7 +3838,7 @@ programs/winetest/build.rc: dummy @build=\"STRINGTABLE { 1 \\\"\`GIT_DIR=${wine_srcdir}.git git rev-parse HEAD 2>/dev/null\`\\\" }\" && (echo \$\$build | cmp -s - \$[@]) || echo \$\$build >\$[@] || (rm -f \$[@] && exit 1) dlls/wineandroid.drv/wine-debug.apk: dlls/wineandroid.drv/build.gradle ${wine_srcdir}dlls/wineandroid.drv/AndroidManifest.xml ${wine_srcdir}dlls/wineandroid.drv/WineActivity.java ${wine_srcdir}dlls/wineandroid.drv/wine.svg cd dlls/wineandroid.drv && gradle -q -Psrcdir=$srcdir assembleDebug - mv dlls/wineandroid.drv/build/outputs/apk/wine-debug.apk \$[@]]) + mv dlls/wineandroid.drv/build/outputs/apk/debug/wine-debug.apk \$[@]]) dnl Misc rules -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10354
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Download and use a local Gradle distribution and Android SDK components to build the in-tree APK target, avoiding reliance on system installations. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- configure | 31 ++++++++++++++++++++++++++++--- configure.ac | 31 ++++++++++++++++++++++++++++--- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/configure b/configure index bf8ee741b70..ef50a1e9298 100755 --- a/configure +++ b/configure @@ -24684,9 +24684,34 @@ dlls/ntdll/unix/version.c: dummy @version=\`(GIT_DIR=${wine_srcdir}.git git describe HEAD 2>/dev/null || echo \"wine-\$(PACKAGE_VERSION)\") | sed -n -e '\$\$s/\(.*\)/const char wine_build[] = \"\\1\";/p'\` && (echo \$\$version | cmp -s - \$@) || echo \$\$version >\$@ || (rm -f \$@ && exit 1) programs/winetest/build.rc: dummy @build=\"STRINGTABLE { 1 \\\"\`GIT_DIR=${wine_srcdir}.git git rev-parse HEAD 2>/dev/null\`\\\" }\" && (echo \$\$build | cmp -s - \$@) || echo \$\$build >\$@ || (rm -f \$@ && exit 1) -dlls/wineandroid.drv/wine-debug.apk: dlls/wineandroid.drv/build.gradle ${wine_srcdir}dlls/wineandroid.drv/AndroidManifest.xml ${wine_srcdir}dlls/wineandroid.drv/WineActivity.java ${wine_srcdir}dlls/wineandroid.drv/wine.svg - cd dlls/wineandroid.drv && gradle -q -Psrcdir=$srcdir assembleDebug - mv dlls/wineandroid.drv/build/outputs/apk/debug/wine-debug.apk \$@" + +GRADLE_VERSION := 9.4.0 +dlls/wineandroid.drv/gradle-\$(GRADLE_VERSION)-bin.zip: + @curl -L -o \$@.tmp https://services.gradle.org/distributions/gradle-\$(GRADLE_VERSION)-bin.zip + @mv -f \$@.tmp \$@ + +dlls/wineandroid.drv/gradle/bin/gradle: dlls/wineandroid.drv/gradle-\$(GRADLE_VERSION)-bin.zip + @rm -rf dlls/wineandroid.drv/gradle + @unzip -q dlls/wineandroid.drv/gradle-\$(GRADLE_VERSION)-bin.zip -d dlls/wineandroid.drv + @mv -f dlls/wineandroid.drv/gradle-\$(GRADLE_VERSION) dlls/wineandroid.drv/gradle + @touch -c \$@ + +dlls/wineandroid.drv/cmdline-tools.zip: + @curl -L -o \$@.tmp https://dl.google.com/android/repository/commandlinetools-linux-7583922_late... + @mv -f \$@.tmp \$@ + +dlls/wineandroid.drv/android/build-tools: dlls/wineandroid.drv/cmdline-tools.zip + @rm -f dlls/wineandroid.drv/android + @mkdir -p dlls/wineandroid.drv/android + @unzip -q dlls/wineandroid.drv/cmdline-tools.zip -d dlls/wineandroid.drv/android >/dev/null + @chmod +x dlls/wineandroid.drv/android/cmdline-tools/bin/sdkmanager + @(yes || true) | dlls/wineandroid.drv/android/cmdline-tools/bin/sdkmanager --sdk_root=\"$(pwd)/dlls/wineandroid.drv/android\" --licenses >/dev/null + @dlls/wineandroid.drv/android/cmdline-tools/bin/sdkmanager --sdk_root=\"$(pwd)/dlls/wineandroid.drv/android\" --install \"platform-tools\" \"platforms;android-35\" \"build-tools;35.0.0\" + @touch -c \$@ + +dlls/wineandroid.drv/wine-debug.apk: dlls/wineandroid.drv/build.gradle ${wine_srcdir}dlls/wineandroid.drv/AndroidManifest.xml ${wine_srcdir}dlls/wineandroid.drv/WineActivity.java ${wine_srcdir}dlls/wineandroid.drv/wine.svg dlls/wineandroid.drv/android/build-tools dlls/wineandroid.drv/gradle/bin/gradle + @cd dlls/wineandroid.drv && env ANDROID_HOME=$(pwd)/dlls/wineandroid.drv/android $(pwd)/dlls/wineandroid.drv/gradle/bin/gradle -q -Psrcdir=$srcdir assembleDebug + @mv dlls/wineandroid.drv/build/outputs/apk/debug/wine-debug.apk \$@" EXTERNAL_SUBDIRS="libs/capstone libs/c++ libs/c++abi libs/faudio libs/fluidsynth libs/gsm libs/icucommon libs/icui18n libs/jpeg libs/jxr libs/lcms2 libs/ldap libs/mpg123 libs/musl libs/png libs/tiff libs/tomcrypt libs/unwind libs/vkd3d libs/xml2 libs/xslt libs/zlib libs/compiler-rt" diff --git a/configure.ac b/configure.ac index 7e801fa8af7..d58a7a68a2b 100644 --- a/configure.ac +++ b/configure.ac @@ -3836,9 +3836,34 @@ WINE_APPEND_RULE( @version=\`(GIT_DIR=${wine_srcdir}.git git describe HEAD 2>/dev/null || echo \"wine-\$(PACKAGE_VERSION)\") | sed -n -e '\$\$s/\(.*\)/const char wine_build[[]] = \"\\1\";/p'\` && (echo \$\$version | cmp -s - \$[@]) || echo \$\$version >\$[@] || (rm -f \$[@] && exit 1) programs/winetest/build.rc: dummy @build=\"STRINGTABLE { 1 \\\"\`GIT_DIR=${wine_srcdir}.git git rev-parse HEAD 2>/dev/null\`\\\" }\" && (echo \$\$build | cmp -s - \$[@]) || echo \$\$build >\$[@] || (rm -f \$[@] && exit 1) -dlls/wineandroid.drv/wine-debug.apk: dlls/wineandroid.drv/build.gradle ${wine_srcdir}dlls/wineandroid.drv/AndroidManifest.xml ${wine_srcdir}dlls/wineandroid.drv/WineActivity.java ${wine_srcdir}dlls/wineandroid.drv/wine.svg - cd dlls/wineandroid.drv && gradle -q -Psrcdir=$srcdir assembleDebug - mv dlls/wineandroid.drv/build/outputs/apk/debug/wine-debug.apk \$[@]]) + +GRADLE_VERSION := 9.4.0 +dlls/wineandroid.drv/gradle-\$(GRADLE_VERSION)-bin.zip: + @curl -L -o \$@.tmp https://services.gradle.org/distributions/gradle-\$(GRADLE_VERSION)-bin.zip + @mv -f \$@.tmp \$@ + +dlls/wineandroid.drv/gradle/bin/gradle: dlls/wineandroid.drv/gradle-\$(GRADLE_VERSION)-bin.zip + @rm -rf dlls/wineandroid.drv/gradle + @unzip -q dlls/wineandroid.drv/gradle-\$(GRADLE_VERSION)-bin.zip -d dlls/wineandroid.drv + @mv -f dlls/wineandroid.drv/gradle-\$(GRADLE_VERSION) dlls/wineandroid.drv/gradle + @touch -c \$@ + +dlls/wineandroid.drv/cmdline-tools.zip: + @curl -L -o \$@.tmp https://dl.google.com/android/repository/commandlinetools-linux-7583922_late... + @mv -f \$@.tmp \$@ + +dlls/wineandroid.drv/android/build-tools: dlls/wineandroid.drv/cmdline-tools.zip + @rm -f dlls/wineandroid.drv/android + @mkdir -p dlls/wineandroid.drv/android + @unzip -q dlls/wineandroid.drv/cmdline-tools.zip -d dlls/wineandroid.drv/android >/dev/null + @chmod +x dlls/wineandroid.drv/android/cmdline-tools/bin/sdkmanager + @(yes || true) | dlls/wineandroid.drv/android/cmdline-tools/bin/sdkmanager --sdk_root=\"$(pwd)/dlls/wineandroid.drv/android\" --licenses >/dev/null + @dlls/wineandroid.drv/android/cmdline-tools/bin/sdkmanager --sdk_root=\"$(pwd)/dlls/wineandroid.drv/android\" --install \"platform-tools\" \"platforms;android-35\" \"build-tools;35.0.0\" + @touch -c \$@ + +dlls/wineandroid.drv/wine-debug.apk: dlls/wineandroid.drv/build.gradle ${wine_srcdir}dlls/wineandroid.drv/AndroidManifest.xml ${wine_srcdir}dlls/wineandroid.drv/WineActivity.java ${wine_srcdir}dlls/wineandroid.drv/wine.svg dlls/wineandroid.drv/android/build-tools dlls/wineandroid.drv/gradle/bin/gradle + @cd dlls/wineandroid.drv && env ANDROID_HOME=$(pwd)/dlls/wineandroid.drv/android $(pwd)/dlls/wineandroid.drv/gradle/bin/gradle -q -Psrcdir=$srcdir assembleDebug + @mv dlls/wineandroid.drv/build/outputs/apk/debug/wine-debug.apk \$[@]]) dnl Misc rules -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10354
Now everything should be in place. If you don’t think downloading Gradle and the Android SDK as part of the build process is a good idea, feel free to drop the last commit. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10354#note_133389
The last commit is mainly there in case you consider having a working in-tree build check more important than strictly avoiding downloads during the configure/make process. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10354#note_133391
Thanks. Yes, that last commit is not something we want to do in the makefile. Most likely it would belong on a wiki page explaining how to download/extract the tools and set the right environment variables. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10354#note_133407
So in that case, should we assume that developers are expected to rely on the external packaging script for a proper build? Referencing:
Well, if we can't build an APK we need to find some other way to test the build. If you make a change to the Java code, you should be able to test that it compiles without having to run an external packaging script.
I don't think the build process should be downloading anything. But yeah, I understand that gradle is a huge PITA to work with...
Given that, it might make sense to add a reference to the official script in the winehq/tools repo (e.g. from Makefile.am and the WINE_APPEND_RULE block in configure.ac), so it’s easier to discover. Alternatively, we could probe for a suitable Gradle/AGP setup and fail early with a clear and concise error message if it’s missing, instead of attempting a build that cannot succeed. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10354#note_133412
So in that case, should we assume that developers are expected to rely on the external packaging script for a proper build?
To build a full package, yes. To test the build, they can run the commands that you had in the makefile, which can be documented on the wiki. For instance, I have a script that builds various configuration before merging MRs (in fact I used to test Android too, until it got broken). In that setup I would download gradle etc. once, and setup the environment to build Wine with it. I wouldn't want to have to download gradle on every build, nor to have to store it inside the Wine tree.
Alternatively, we could probe for a suitable Gradle/AGP setup and fail early with a clear and concise error message if it’s missing, instead of attempting a build that cannot succeed.
Sure. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10354#note_133413
participants (3)
-
Alexandre Julliard (@julliard) -
Twaik Yont -
Twaik Yont (@twaik)