From fe8017734b48eaf092ebe4e4930c4d9a48a586c6 Mon Sep 17 00:00:00 2001 From: crueter Date: Wed, 3 Dec 2025 06:40:11 +0100 Subject: [PATCH] [cmake, externals] android x86_64 support (#3086) Updates all of our bundled CI deps to support android x86_64, adds a build flavor thereof (`chromeOS`), and also adds sirit mingw support. The new FFmpeg package is built in a much better way that actually makes it identically built to the other CI packages, meaning we now have real 8.0.0 support, no need for libvpx/cpu_features/all that other crap. PLUS, we can now statically link it! Hooray! It's also built with MediaCodec support so in the future we can work on that. Rewrote the android build script too, plus added a copyFlavorTypeOutput target that assembles and copies the APK. The code behind it sucks because I'm not great with Gradle but hey, it works. Testers: please test everything related to video decoding. VP9 and h264, games that normally suck with their prerendered stuff, make sure I didn't nuke it to oblivion, etc. Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3086 Reviewed-by: Caio Oliveira Reviewed-by: Lizzie --- .ci/android/build.sh | 114 +++++++++++++++++++++++++++++-- .ci/android/package.sh | 22 ------ CMakeLists.txt | 16 +++-- CMakeModules/CPMUtil.cmake | 8 ++- cpmfile.json | 2 +- docs/build/Android.md | 62 ++++++++++++++--- externals/cpmfile.json | 10 +-- externals/ffmpeg/CMakeLists.txt | 10 --- externals/ffmpeg/cpmfile.json | 6 +- src/android/app/build.gradle.kts | 71 ++++++++++++++++--- tools/cpm/download.sh | 7 +- 11 files changed, 253 insertions(+), 75 deletions(-) delete mode 100755 .ci/android/package.sh diff --git a/.ci/android/build.sh b/.ci/android/build.sh index 836faa38d5..07cda2829f 100755 --- a/.ci/android/build.sh +++ b/.ci/android/build.sh @@ -1,21 +1,121 @@ -#!/bin/bash -e +#!/bin/sh -e # SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later -export NDK_CCACHE=$(which ccache) +NUM_JOBS=$(nproc 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || echo 2) +export CMAKE_BUILD_PARALLEL_LEVEL="${NUM_JOBS}" +ARTIFACTS_DIR="$PWD/artifacts" -if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then +: "${CCACHE:=false}" +RETURN=0 + +usage() { + cat < Build flavor (variable: TARGET) + Valid values are: legacy, optimized, standard, chromeos + Default: standard + -b, --build-type Build type (variable: TYPE) + Valid values are: Release, RelWithDebInfo, Debug + Default: Debug + +Extra arguments are passed to CMake (e.g. -DCMAKE_OPTION_NAME=VALUE) +Set the CCACHE variable to "true" to enable build caching. +The APK and AAB will be output into "artifacts". + +EOF + + exit "$RETURN" +} + +die() { + echo "-- ! $*" >&2 + RETURN=1 usage +} + +target() { + [ -z "$1" ] && die "You must specify a valid target." + + TARGET="$1" +} + +type() { + [ -z "$1" ] && die "You must specify a valid type." + + TYPE="$1" +} + +while true; do + case "$1" in + -r|--release) DEVEL=false ;; + -t|--target) target "$2"; shift ;; + -b|--build-type) type "$2"; shift ;; + -h|--help) usage ;; + *) break ;; + esac + + shift +done + +: "${TARGET:=standard}" +: "${TYPE:=Release}" +: "${DEVEL:=true}" + +TARGET_LOWER=$(echo "$TARGET" | tr '[:upper:]' '[:lower:]') + +case "$TARGET_LOWER" in + legacy) FLAVOR=Legacy ;; + optimized) FLAVOR=GenshinSpoof ;; + standard) FLAVOR=Mainline ;; + chromeos) FLAVOR=ChromeOS ;; + *) die "Invalid build flavor $TARGET." +esac + +case "$TYPE" in + RelWithDebInfo|Release|Debug) ;; + *) die "Invalid build type $TYPE." +esac + +LOWER_FLAVOR=$(echo "$FLAVOR" | sed 's/./\L&/') +LOWER_TYPE=$(echo "$TYPE" | sed 's/./\L&/') + +if [ -n "${ANDROID_KEYSTORE_B64}" ]; then export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks" - base64 --decode <<< "${ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}" + echo "${ANDROID_KEYSTORE_B64}" | base64 --decode > "${ANDROID_KEYSTORE_FILE}" + SHA1SUM=$(keytool -list -v -storepass "${ANDROID_KEYSTORE_PASS}" -keystore "${ANDROID_KEYSTORE_FILE}" | grep SHA1 | cut -d " " -f3) + echo "-- Keystore SHA1 is ${SHA1SUM}" fi cd src/android chmod +x ./gradlew -./gradlew assembleMainlineRelease -./gradlew bundleMainlineRelease +set -- "$@" -DUSE_CCACHE="${CCACHE}" +[ "$DEVEL" != "true" ] && set -- "$@" -DENABLE_UPDATE_CHECKER=ON -if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then +echo "-- building..." + +./gradlew "copy${FLAVOR}${TYPE}Outputs" \ + -Dorg.gradle.caching="${CCACHE}" \ + -Dorg.gradle.parallel="${CCACHE}" \ + -Dorg.gradle.workers.max="${NUM_JOBS}" \ + -PYUZU_ANDROID_ARGS="$*" \ + --info + +if [ -n "${ANDROID_KEYSTORE_B64}" ]; then rm "${ANDROID_KEYSTORE_FILE}" fi + +echo "-- Done! APK and AAB artifacts are in ${ARTIFACTS_DIR}" + +ls -l "${ARTIFACTS_DIR}/" diff --git a/.ci/android/package.sh b/.ci/android/package.sh deleted file mode 100755 index 50b7bbc332..0000000000 --- a/.ci/android/package.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh - -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project -# SPDX-License-Identifier: GPL-3.0-or-later - -GITDATE="$(git show -s --date=short --format='%ad' | sed 's/-//g')" -GITREV="$(git show -s --format='%h')" -ARTIFACTS_DIR="$PWD/artifacts" -mkdir -p "${ARTIFACTS_DIR}/" - -REV_NAME="eden-android-${GITDATE}-${GITREV}" -BUILD_FLAVOR="mainline" -BUILD_TYPE_LOWER="release" -BUILD_TYPE_UPPER="Release" - -cp src/android/app/build/outputs/apk/"${BUILD_FLAVOR}/${BUILD_TYPE_LOWER}/app-${BUILD_FLAVOR}-${BUILD_TYPE_LOWER}.apk" \ - "${ARTIFACTS_DIR}/${REV_NAME}.apk" || echo "APK not found" - -cp src/android/app/build/outputs/bundle/"${BUILD_FLAVOR}${BUILD_TYPE_UPPER}"/"app-${BUILD_FLAVOR}-${BUILD_TYPE_LOWER}.aab" \ - "${ARTIFACTS_DIR}/${REV_NAME}.aab" || echo "AAB not found" - -ls -la "${ARTIFACTS_DIR}/" diff --git a/CMakeLists.txt b/CMakeLists.txt index 4afbc13683..4a108e47af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -436,10 +436,10 @@ if (ENABLE_OPENSSL) option(YUZU_USE_BUNDLED_OPENSSL "Download bundled OpenSSL build" ${DEFAULT_YUZU_USE_BUNDLED_OPENSSL}) endif() +# TODO(crueter): CPM this if (ANDROID AND YUZU_DOWNLOAD_ANDROID_VVL) - # TODO(crueter): CPM this - set(vvl_version "1.4.321.0") - set(vvl_zip_file "${CMAKE_BINARY_DIR}/externals/vvl-android.zip") + set(vvl_version "1.4.328.0") + set(vvl_zip_file "${CMAKE_BINARY_DIR}/externals/vvl-android-${vvl_version}.zip") if (NOT EXISTS "${vvl_zip_file}") # Download and extract validation layer release to externals directory set(vvl_base_url "https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download") @@ -450,8 +450,14 @@ if (ANDROID AND YUZU_DOWNLOAD_ANDROID_VVL) endif() # Copy the arm64 binary to src/android/app/main/jniLibs - set(vvl_lib_path "${CMAKE_CURRENT_SOURCE_DIR}/src/android/app/src/main/jniLibs/arm64-v8a/") - file(COPY "${CMAKE_BINARY_DIR}/externals/android-binaries-${vvl_version}/arm64-v8a/libVkLayer_khronos_validation.so" + if (ARCHITECTURE_arm64) + set(vvl_abi arm64-v8a) + elseif(ARCHITECTURE_x86_64) + set(vvl_abi x86_64) + endif() + + set(vvl_lib_path "${CMAKE_CURRENT_SOURCE_DIR}/src/android/app/src/main/jniLibs/${vvl_abi}/") + file(COPY "${CMAKE_BINARY_DIR}/externals/android-binaries-${vvl_version}/${vvl_abi}/libVkLayer_khronos_validation.so" DESTINATION "${vvl_lib_path}") endif() diff --git a/CMakeModules/CPMUtil.cmake b/CMakeModules/CPMUtil.cmake index 78bab05030..53b9e24079 100644 --- a/CMakeModules/CPMUtil.cmake +++ b/CMakeModules/CPMUtil.cmake @@ -603,8 +603,12 @@ function(AddCIPackage) add_ci_package(mingw-arm64) endif() - if (ANDROID AND NOT "android" IN_LIST DISABLED_PLATFORMS) - add_ci_package(android) + if((ANDROID AND ARCHITECTURE_x86_64) AND NOT "android-x86_64" IN_LIST DISABLED_PLATFORMS) + add_ci_package(android-x86_64) + endif() + + if((ANDROID AND ARCHITECTURE_arm64) AND NOT "android-aarch64" IN_LIST DISABLED_PLATFORMS) + add_ci_package(android-aarch64) endif() if(PLATFORM_SUN AND NOT "solaris-amd64" IN_LIST DISABLED_PLATFORMS) diff --git a/cpmfile.json b/cpmfile.json index 6cf7ed1a41..c00f955087 100644 --- a/cpmfile.json +++ b/cpmfile.json @@ -4,7 +4,7 @@ "package": "OpenSSL", "name": "openssl", "repo": "crueter-ci/OpenSSL", - "version": "3.6.0-e3608d80df", + "version": "3.6.0-965d6279e8", "min_version": "1.1.1" }, "boost": { diff --git a/docs/build/Android.md b/docs/build/Android.md index 5805ed2797..638d429328 100644 --- a/docs/build/Android.md +++ b/docs/build/Android.md @@ -1,30 +1,37 @@ -# Note: These build instructions are a work-in-progress. +# Android ## Dependencies + * [Android Studio](https://developer.android.com/studio) * [NDK 27+ and CMake 3.22.1](https://developer.android.com/studio/projects/install-ndk#default-version) * [Git](https://git-scm.com/download) -### WINDOWS ONLY - Additional Dependencies - * **[Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/)** - **Make sure to select "Desktop development with C++" support in the installer. Make sure to update to the latest version if already installed.** - * **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** - **Make sure to select Latest SDK.** - - A convenience script to install the latest SDK is provided in `.ci\windows\install-vulkan-sdk.ps1`. +## WINDOWS ONLY - Additional Dependencies + +* **[Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/)** - **Make sure to select "Desktop development with C++" support in the installer. Make sure to update to the latest version if already installed.** +* **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** - **Make sure to select Latest SDK.** + * A convenience script to install the latest SDK is provided in `.ci\windows\install-vulkan-sdk.ps1`. ## Cloning Eden with Git -``` + +```sh git clone --recursive https://git.eden-emu.dev/eden-emu/eden.git ``` -Eden by default will be cloned into - + +Eden by default will be cloned into: + * `C:\Users\\eden` on Windows * `~/eden` on Linux and macOS ## Building + 1. Start Android Studio, on the startup dialog select `Open`. 2. Navigate to the `eden/src/android` directory and click on `OK`. 3. In `Build > Select Build Variant`, select `release` or `relWithDebInfo` as the "Active build variant". 4. Build the project with `Build > Make Project` or run it on an Android device with `Run > Run 'app'`. ## Building with Terminal + 1. Download the SDK and NDK from Android Studio. 2. Navigate to SDK and NDK paths. 3. Then set ANDROID_SDK_ROOT and ANDROID_NDK_ROOT in terminal via @@ -38,7 +45,44 @@ Eden by default will be cloned into - Remember to have a Java SDK installed if not already, on Debian and similar this is done with `sudo apt install openjdk-17-jdk`. ### Script -A convenience script for building is provided in `.ci/android/build.sh`. The built APK can be put into an `artifacts` directory via `.ci/android/package.sh`. On Windows, these must be done in the Git Bash or MinGW terminal. + +A convenience script for building is provided in `.ci/android/build.sh`. On Windows, this must be run in Git Bash or MSYS2. This script provides the following options: + +```txt +Usage: build.sh [-c|--chromeos] [-t|--target FLAVOR] [-b|--build-type BUILD_TYPE] + [-h|--help] [-r|--release] [extra options] + +Build script for Android. +Associated variables can be set outside the script, +and will apply both to this script and the packaging script. +bool values are "true" or "false" + +Options: + -c, --chromeos Build for ChromeOS (x86_64) (variable: CHROMEOS, bool) + Default: false + -r, --release Enable update checker. If set, sets the DEVEL bool variable to false. + By default, DEVEL is true. + -t, --target Build flavor (variable: TARGET) + Valid values are: legacy, optimized, standard + Default: standard + -b, --build-type Build type (variable: TYPE) + Valid values are: Release, RelWithDebInfo, Debug + Default: Debug + +Extra arguments are passed to CMake (e.g. -DCMAKE_OPTION_NAME=VALUE) +Set the CCACHE variable to "true" to enable build caching. +The APK and AAB will be output into "artifacts". +``` + +Examples: + +* Build legacy release with update checker for ChromeOS: + * `.ci/android/build.sh -c -r -t legacy` +* Build standard release with debug info without update checker for phones: + * `.ci/android/build.sh -b RelWithDebInfo` +* Build optimized release with update checker: + * `.ci/android/build.sh -r -t optimized` ### Additional Resources -https://developer.android.com/studio/intro + + diff --git a/externals/cpmfile.json b/externals/cpmfile.json index ee5ccb451e..7f5af5dfce 100644 --- a/externals/cpmfile.json +++ b/externals/cpmfile.json @@ -9,7 +9,7 @@ }, "sirit": { "repo": "eden-emulator/sirit", - "git_version": "1.0.2", + "git_version": "1.0.3", "tag": "v%VERSION%", "artifact": "sirit-source-%VERSION%.tar.zst", "hash_suffix": "sha512sum", @@ -23,11 +23,7 @@ "package": "sirit", "name": "sirit", "repo": "eden-emulator/sirit", - "version": "1.0.2", - "disabled_platforms": [ - "mingw-amd64", - "mingw-arm64" - ] + "version": "1.0.3" }, "httplib": { "repo": "yhirose/cpp-httplib", @@ -167,7 +163,7 @@ "package": "SDL2", "name": "SDL2", "repo": "crueter-ci/SDL2", - "version": "2.32.10-38e0094637", + "version": "2.32.10-a65111bd2d", "min_version": "2.26.4" }, "catch2": { diff --git a/externals/ffmpeg/CMakeLists.txt b/externals/ffmpeg/CMakeLists.txt index 58461d8934..d71613db8c 100644 --- a/externals/ffmpeg/CMakeLists.txt +++ b/externals/ffmpeg/CMakeLists.txt @@ -114,16 +114,6 @@ if (UNIX AND NOT ANDROID) endif() if (YUZU_USE_BUNDLED_FFMPEG) - # MSVC conflicts with ksuser otherwise - # MinGW has the funny quirk of requiring avutil after avcodec - # Android needs some deps to be compiled with PIC (TODO) - # TODO(crueter) fix - if (ANDROID) - set(BUILD_SHARED_LIBS ON) - else() - set(BUILD_SHARED_LIBS OFF) - endif() - AddJsonPackage(ffmpeg-ci) set(FFmpeg_INCLUDE_DIR diff --git a/externals/ffmpeg/cpmfile.json b/externals/ffmpeg/cpmfile.json index 0baeeb4713..0d0a612eda 100644 --- a/externals/ffmpeg/cpmfile.json +++ b/externals/ffmpeg/cpmfile.json @@ -1,8 +1,8 @@ { "ffmpeg": { "repo": "FFmpeg/FFmpeg", - "sha": "c2184b65d2", - "hash": "007b1ccdd4d3ea3324835258d9a255103253bd66edb442b12d9c60dca85149cad52136a3b3120e5094115b6a3d9e80eeacbf9c07e5ffafc9ac459614d5fa3b22", + "sha": "ddf443f1e9", + "hash": "ded1c313843f23805102565bd3ca92602fb9c2951e059ca5e1a486ab3ef7d589acccf3cde05c5ff0cfc5199c3a261dccb4d2a93254e585824850696fb41a292e", "bundled": true }, "ffmpeg-ci": { @@ -10,7 +10,7 @@ "package": "FFmpeg", "name": "ffmpeg", "repo": "crueter-ci/FFmpeg", - "version": "8.0-be99d2c0b2", + "version": "8.0-ddf443f1e9", "min_version": "4.1" } } diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index 01a413261e..87d815c92c 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -4,10 +4,11 @@ // SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -import android.annotation.SuppressLint +// import android.annotation.SuppressLint import kotlin.collections.setOf import org.jlleitschuh.gradle.ktlint.reporter.ReporterType import com.github.triplet.gradle.androidpublisher.ReleaseStatus +import org.gradle.api.tasks.Copy plugins { id("com.android.application") @@ -63,11 +64,6 @@ android { versionName = getGitVersion() versionCode = autoVersion - ndk { - @SuppressLint("ChromeOsAbiSupport") - abiFilters += listOf("arm64-v8a") - } - externalNativeBuild { cmake { val extraCMakeArgs = @@ -127,7 +123,7 @@ android { isMinifyEnabled = true isDebuggable = false proguardFiles( - getDefaultProguardFile("proguard-android.txt"), + getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } @@ -139,7 +135,7 @@ android { signingConfig = signingConfigs.getByName("default") isDebuggable = true proguardFiles( - getDefaultProguardFile("proguard-android.txt"), + getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) versionNameSuffix = "-relWithDebInfo" @@ -163,12 +159,20 @@ android { create("mainline") { dimension = "version" resValue("string", "app_name_suffixed", "Eden") + + ndk { + abiFilters += listOf("arm64-v8a") + } } create("genshinSpoof") { dimension = "version" resValue("string", "app_name_suffixed", "Eden Optimized") applicationId = "com.miHoYo.Yuanshen" + + ndk { + abiFilters += listOf("arm64-v8a") + } } create("legacy") { @@ -187,6 +191,25 @@ android { res.srcDirs("src/main/legacy") } } + + ndk { + abiFilters += listOf("arm64-v8a") + } + } + + create("chromeOS") { + dimension = "version" + resValue("string", "app_name_suffixed", "Eden") + + ndk { + abiFilters += listOf("x86_64") + } + + externalNativeBuild { + cmake { + abiFilters("x86_64") + } + } } } @@ -321,3 +344,35 @@ fun getGitVersion(): String { } return versionName.ifEmpty { "0.0" } } + +afterEvaluate { + val artifactsDir = layout.projectDirectory.dir("../../../artifacts") + val outputsDir = layout.buildDirectory.dir("outputs").get() + + android.applicationVariants.forEach { variant -> + val variantName = variant.name + val variantTask = variantName.replaceFirstChar { it.uppercaseChar() } + + val flavor = variant.flavorName + val type = variant.buildType.name + + val baseName = "app-$flavor-$type" + + val apkFile = outputsDir.file("apk/$flavor/$type/$baseName.apk") + val aabFile = outputsDir.file("bundle/$variantName/$baseName.aab") + + val taskName = "copy${variantTask}Outputs" + + tasks.register(taskName) { + group = "publishing" + description = "Copy APK and AAB for $variantName to $artifactsDir" + + from(apkFile) + from(aabFile) + into(artifactsDir) + + dependsOn("assemble${variantTask}") + dependsOn("bundle${variantTask}") + } + } +} diff --git a/tools/cpm/download.sh b/tools/cpm/download.sh index 9793db0c04..bc436ba0f1 100755 --- a/tools/cpm/download.sh +++ b/tools/cpm/download.sh @@ -65,7 +65,12 @@ ci_package() { echo "-- CI package $PACKAGE_NAME" - for platform in windows-amd64 windows-arm64 mingw-amd64 mingw-arm64 android solaris-amd64 freebsd-amd64 openbsd-amd64 linux-amd64 linux-aarch64 macos-universal; do + for platform in windows-amd64 windows-arm64 \ + mingw-amd64 mingw-arm64 \ + android-aarch64 android-x86_64 \ + solaris-amd64 freebsd-amd64 openbsd-amd64 \ + linux-amd64 linux-aarch64 \ + macos-universal; do echo "-- * platform $platform" case $DISABLED in