[PATCH v3 0/2] MR10438: configure: fail early on invalid Gradle/SDK
This change is motivated by the long-standing issue that most Linux distributions ship an outdated Gradle, which is often too old for modern Android builds. In typical Android projects this is solved by using the Gradle wrapper, which ensures a compatible Gradle version is downloaded and used automatically. However, Wine cannot rely on the Gradle wrapper for this purpose, and integrating automatic downloads into the build system is not acceptable, since the build process must not fetch external resources. As a result, Gradle and the Android SDK effectively become external prerequisites that must be provided by the user or by higher-level build scripts. Previously, missing or outdated dependencies would only surface later during the build, often resulting in confusing or hard-to-diagnose errors. This change makes these requirements explicit and enforces them at configure time: - `gradle` is detected, its version is checked, and the resolved path is cached. - `ANDROID_HOME` is required and validated against the expected SDK layout. - A minimal sanity check ensures that a usable `build-tools` installation is present. This allows the build to fail early with clear error messages if the environment is not properly set up, instead of failing deep inside the Gradle build. Additionally, `GRADLE` and `ANDROID_HOME` are cached by configure, so they do not need to be re-specified when invoking make or even present in environment. -- v3: wineandroid: print hint for full APK build when using dummy target https://gitlab.winehq.org/wine/wine/-/merge_requests/10438
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Detect and cache GRADLE and ANDROID_HOME so they do not need to be provided again when running make, and fail early during configure if they are missing or invalid. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- configure | 165 ++++++++++++++++++++++++++- configure.ac | 39 ++++++- dlls/wineandroid.drv/build.gradle.in | 6 +- 3 files changed, 205 insertions(+), 5 deletions(-) diff --git a/configure b/configure index 981fb77c455..dc58dec120c 100755 --- a/configure +++ b/configure @@ -579,6 +579,66 @@ as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated as_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g" as_tr_sh="eval sed '$as_sed_sh'" # deprecated +as_awk_strverscmp=' + # Use only awk features that work with 7th edition Unix awk (1978). + # My, what an old awk you have, Mr. Solaris! + END { + while (length(v1) && length(v2)) { + # Set d1 to be the next thing to compare from v1, and likewise for d2. + # Normally this is a single character, but if v1 and v2 contain digits, + # compare them as integers and fractions as strverscmp does. + if (v1 ~ /^[0-9]/ && v2 ~ /^[0-9]/) { + # Split v1 and v2 into their leading digit string components d1 and d2, + # and advance v1 and v2 past the leading digit strings. + for (len1 = 1; substr(v1, len1 + 1) ~ /^[0-9]/; len1++) continue + for (len2 = 1; substr(v2, len2 + 1) ~ /^[0-9]/; len2++) continue + d1 = substr(v1, 1, len1); v1 = substr(v1, len1 + 1) + d2 = substr(v2, 1, len2); v2 = substr(v2, len2 + 1) + if (d1 ~ /^0/) { + if (d2 ~ /^0/) { + # Compare two fractions. + while (d1 ~ /^0/ && d2 ~ /^0/) { + d1 = substr(d1, 2); len1-- + d2 = substr(d2, 2); len2-- + } + if (len1 != len2 && ! (len1 && len2 && substr(d1, 1, 1) == substr(d2, 1, 1))) { + # The two components differ in length, and the common prefix + # contains only leading zeros. Consider the longer to be less. + d1 = -len1 + d2 = -len2 + } else { + # Otherwise, compare as strings. + d1 = "x" d1 + d2 = "x" d2 + } + } else { + # A fraction is less than an integer. + exit 1 + } + } else { + if (d2 ~ /^0/) { + # An integer is greater than a fraction. + exit 2 + } else { + # Compare two integers. + d1 += 0 + d2 += 0 + } + } + } else { + # The normal case, without worrying about digits. + d1 = substr(v1, 1, 1); v1 = substr(v1, 2) + d2 = substr(v2, 1, 1); v2 = substr(v2, 2) + } + if (d1 < d2) exit 1 + if (d1 > d2) exit 2 + } + # Beware Solaris /usr/xgp4/bin/awk (at least through Solaris 10), + # which mishandles some comparisons of empty strings to integers. + if (length(v2)) exit 1 + if (length(v1)) exit 2 + } +' test -n "$DJDIR" || exec 7<&0 </dev/null exec 6>&1 @@ -761,6 +821,8 @@ CXX_PE_CFLAGS CAPSTONE_PE_LIBS CAPSTONE_PE_CFLAGS MINGW_PKG_CONFIG +ANDROID_HOME +GRADLE WINELOADER_DEPENDS ac_ct_OBJC OBJCFLAGS @@ -1853,6 +1915,7 @@ CXXFLAGS CCC OBJC OBJCFLAGS +ANDROID_HOME CAPSTONE_PE_CFLAGS CAPSTONE_PE_LIBS CXX_PE_CFLAGS @@ -2664,6 +2727,8 @@ Some influential environment variables: CXXFLAGS C++ compiler flags OBJC Objective C compiler command OBJCFLAGS Objective C compiler flags + ANDROID_HOME + Path to the Android SDK CAPSTONE_PE_CFLAGS C compiler flags for the PE capstone, overriding the bundled version @@ -13072,6 +13137,102 @@ fi aarch64) exec_prefix='${prefix}/arm64-v8a' ;; esac fi + + GRADLE_MIN_VERSION=9.4.0 + # Extract the first word of "gradle", so it can be a program name with args. +set dummy gradle; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_path_GRADLE+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) case $GRADLE in + [\\/]* | ?:[\\/]*) + ac_cv_path_GRADLE="$GRADLE" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_path_GRADLE="$as_dir$ac_word$ac_exec_ext" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac ;; +esac +fi +GRADLE=$ac_cv_path_GRADLE +if test -n "$GRADLE"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $GRADLE" >&5 +printf "%s\n" "$GRADLE" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + + if test -z "$GRADLE"; then + as_fn_error $? "gradle not found" "$LINENO" 5 + fi + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Gradle version" >&5 +printf %s "checking for Gradle version... " >&6; } + + gradle_version=$("$GRADLE" --version 2>/dev/null | sed -n '/^Gradle /{s/^Gradle //;s/ .*//;p;q}') + + if test -z "$gradle_version"; then + as_fn_error $? "cannot determine Gradle version" "$LINENO" 5 + fi + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $gradle_version" >&5 +printf "%s\n" "$gradle_version" >&6; } + as_arg_v1=$gradle_version +as_arg_v2=$GRADLE_MIN_VERSION +awk "$as_awk_strverscmp" v1="$as_arg_v1" v2="$as_arg_v2" /dev/null +case $? in #( + 1) : + as_fn_error $? "Gradle $GRADLE_MIN_VERSION or newer is required" "$LINENO" 5 ;; #( + 0) : + ;; #( + 2) : + ;; #( + *) : + ;; +esac + + ANDROID_COMPILE_SDK=36 + + + test -n "$ANDROID_HOME" || as_fn_error $? "SDK location not found. Define a valid SDK location with an ANDROID_HOME environment variable; use ANDROID_HOME=/path/to/sdk ./configure or ./configure ANDROID_HOME=/path/to/sdk" "$LINENO" 5 + test -d "$ANDROID_HOME" || as_fn_error $? "ANDROID_HOME does not exist: $ANDROID_HOME" "$LINENO" 5 + test -d "$ANDROID_HOME/platforms/android-$ANDROID_COMPILE_SDK" || as_fn_error $? "Android SDK platform android-$ANDROID_COMPILE_SDK is missing" "$LINENO" 5 + test -d "$ANDROID_HOME/build-tools" || as_fn_error $? "Android SDK build-tools directory is missing: $ANDROID_HOME/build-tools" "$LINENO" 5 + + found=no + for d in "$ANDROID_HOME"/build-tools/*; do + if test -x "$d/aapt2" && test -x "$d/d8" && test -x "$d/zipalign" && test -x "$d/apksigner" && test -f "$d/source.properties"; then + found=yes + fi + done + + test "$found" = yes || as_fn_error $? "no usable Android SDK Build-Tools found" "$LINENO" 5 + + SED_CMD="$SED_CMD -e 's,@GRADLE_MIN_VERSION@,$GRADLE_MIN_VERSION,g' -e 's,@ANDROID_COMPILE_SDK@,$ANDROID_COMPILE_SDK,g'" ;; *) @@ -24696,7 +24857,7 @@ dlls/ntdll/unix/version.c: dummy 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 + cd dlls/wineandroid.drv && env ANDROID_HOME=\"$ANDROID_HOME\" $GRADLE -q -Psrcdir=$srcdir assembleDebug mv dlls/wineandroid.drv/build/outputs/apk/debug/wine-debug.apk \$@" @@ -25589,6 +25750,8 @@ OBJC = $OBJC OBJCFLAGS = $OBJCFLAGS ac_ct_OBJC = $ac_ct_OBJC WINELOADER_DEPENDS = $WINELOADER_DEPENDS +GRADLE = $GRADLE +ANDROID_HOME = $ANDROID_HOME MINGW_PKG_CONFIG = $MINGW_PKG_CONFIG CAPSTONE_PE_CFLAGS = $CAPSTONE_PE_CFLAGS CAPSTONE_PE_LIBS = $CAPSTONE_PE_LIBS diff --git a/configure.ac b/configure.ac index 9a437851996..f9a7216d13a 100644 --- a/configure.ac +++ b/configure.ac @@ -1064,6 +1064,43 @@ case $host_os in aarch64) exec_prefix='${prefix}/arm64-v8a' ;; esac fi + + GRADLE_MIN_VERSION=9.4.0 + AC_PATH_PROG([GRADLE], [gradle]) + + if test -z "$GRADLE"; then + AC_MSG_ERROR([gradle not found]) + fi + + AC_MSG_CHECKING([for Gradle version]) + + gradle_version=$("$GRADLE" --version 2>/dev/null | sed -n '/^Gradle /{s/^Gradle //;s/ .*//;p;q}') + + if test -z "$gradle_version"; then + AC_MSG_ERROR([cannot determine Gradle version]) + fi + + AC_MSG_RESULT([$gradle_version]) + AS_VERSION_COMPARE([$gradle_version], [$GRADLE_MIN_VERSION], [AC_MSG_ERROR([Gradle $GRADLE_MIN_VERSION or newer is required])], [], []) + + ANDROID_COMPILE_SDK=36 + + AC_ARG_VAR([ANDROID_HOME], [Path to the Android SDK]) + test -n "$ANDROID_HOME" || AC_MSG_ERROR([SDK location not found. Define a valid SDK location with an ANDROID_HOME environment variable; use ANDROID_HOME=/path/to/sdk ./configure or ./configure ANDROID_HOME=/path/to/sdk]) + test -d "$ANDROID_HOME" || AC_MSG_ERROR([ANDROID_HOME does not exist: $ANDROID_HOME]) + test -d "$ANDROID_HOME/platforms/android-$ANDROID_COMPILE_SDK" || AC_MSG_ERROR([Android SDK platform android-$ANDROID_COMPILE_SDK is missing]) + test -d "$ANDROID_HOME/build-tools" || AC_MSG_ERROR([Android SDK build-tools directory is missing: $ANDROID_HOME/build-tools]) + + found=no + for d in "$ANDROID_HOME"/build-tools/*; do + if test -x "$d/aapt2" && test -x "$d/d8" && test -x "$d/zipalign" && test -x "$d/apksigner" && test -f "$d/source.properties"; then + found=yes + fi + done + + test "$found" = yes || AC_MSG_ERROR([no usable Android SDK Build-Tools found]) + + SED_CMD="$SED_CMD -e 's,@GRADLE_MIN_VERSION@,$GRADLE_MIN_VERSION,g' -e 's,@ANDROID_COMPILE_SDK@,$ANDROID_COMPILE_SDK,g'" ;; *) @@ -3844,7 +3881,7 @@ WINE_APPEND_RULE( 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 + cd dlls/wineandroid.drv && env ANDROID_HOME=\"$ANDROID_HOME\" $GRADLE -q -Psrcdir=$srcdir assembleDebug mv dlls/wineandroid.drv/build/outputs/apk/debug/wine-debug.apk \$[@]]) dnl Misc rules diff --git a/dlls/wineandroid.drv/build.gradle.in b/dlls/wineandroid.drv/build.gradle.in index 8d0e138c9a2..03cab2741b3 100644 --- a/dlls/wineandroid.drv/build.gradle.in +++ b/dlls/wineandroid.drv/build.gradle.in @@ -18,8 +18,8 @@ * 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") +if (GradleVersion.current() < GradleVersion.version("@GRADLE_MIN_VERSION@")) { + throw new GradleException("Gradle @GRADLE_MIN_VERSION@+ required") } apply plugin: 'com.android.application' @@ -107,7 +107,7 @@ tasks.withType(JavaCompile) android { namespace "org.winehq.wine" - compileSdkVersion 36 + compileSdk @ANDROID_COMPILE_SDK@ defaultConfig { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10438
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Print a message when the dummy wine-debug.apk target is built without packaged assets, pointing to a reference full build script. This clarifies that the target is only a sanity check and helps avoid confusion when the resulting APK is not a complete build. Also document this behavior in Makefile.in. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- configure | 1 + configure.ac | 1 + dlls/wineandroid.drv/Makefile.in | 2 ++ 3 files changed, 4 insertions(+) diff --git a/configure b/configure index dc58dec120c..fb3838c0609 100755 --- a/configure +++ b/configure @@ -24859,6 +24859,7 @@ programs/winetest/build.rc: dummy 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 && env ANDROID_HOME=\"$ANDROID_HOME\" $GRADLE -q -Psrcdir=$srcdir assembleDebug mv dlls/wineandroid.drv/build/outputs/apk/debug/wine-debug.apk \$@" + @(cd dlls/wineandroid.drv && test -d assets && test -d lib) || echo "Dummy APK build succeeded. For a full build example, see: https://gitlab.winehq.org/winehq/tools/-/tree/master/packaging/android" >&2 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 f9a7216d13a..b2bf0099ab7 100644 --- a/configure.ac +++ b/configure.ac @@ -3883,6 +3883,7 @@ programs/winetest/build.rc: dummy 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 && env ANDROID_HOME=\"$ANDROID_HOME\" $GRADLE -q -Psrcdir=$srcdir assembleDebug mv dlls/wineandroid.drv/build/outputs/apk/debug/wine-debug.apk \$[@]]) + @(cd dlls/wineandroid.drv && test -d assets && test -d lib) || echo "Dummy APK build succeeded. For a full build example, see: https://gitlab.winehq.org/winehq/tools/-/tree/master/packaging/android" >&2 dnl Misc rules diff --git a/dlls/wineandroid.drv/Makefile.in b/dlls/wineandroid.drv/Makefile.in index 9a8be0ef90a..2f20575eac7 100644 --- a/dlls/wineandroid.drv/Makefile.in +++ b/dlls/wineandroid.drv/Makefile.in @@ -13,4 +13,6 @@ SOURCES = \ window.c \ wine.svg +# wine-debug.apk is a dummy target used to verify the build. +# For a full build script example, see: https://gitlab.winehq.org/winehq/tools/-/tree/master/packaging/android EXTRA_TARGETS = wine-debug.apk -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10438
Following previous discussion in !10354. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10438#note_133553
participants (2)
-
Twaik Yont -
Twaik Yont (@twaik)