diff --git a/.ci/android/build.sh b/.ci/android/build.sh index 836faa38d5..3a2395ebf4 100755 --- a/.ci/android/build.sh +++ b/.ci/android/build.sh @@ -1,21 +1,135 @@ -#!/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 + 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 + -c|--chromeos) CHROMEOS=true ;; + -r|--release) DEVEL=false ;; + -t|--target) target "$2"; shift ;; + -b|--build-type) type "$2"; shift ;; + -h|--help) usage ;; + *) break ;; + esac + + shift +done + +: "${CHROMEOS:=false}" +: "${TARGET:=standard}" +: "${TYPE:=Release}" +: "${DEVEL:=true}" + +case "$TARGET" in + legacy) FLAVOR=Legacy ;; + optimized) FLAVOR=GenshinSpoof ;; + standard) FLAVOR=Mainline ;; + *) 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 +[ "$CHROMEOS" = "true" ] && ABI=x86_64 +: "${ABI:=arm64-v8a}" + +echo "-- packaging APK" +./gradlew copy${FLAVOR}"${TYPE}Output" \ + -Dorg.gradle.caching="${CCACHE}" \ + -Dorg.gradle.parallel="${CCACHE}" \ + -Dorg.gradle.workers.max="${NUM_JOBS}" \ + -Pandroid.injected.build.abi="${ABI}" \ + -PYUZU_ANDROID_ARGS="$*" + +echo "-- building AAB" +./gradlew bundle${FLAVOR}"${TYPE}" \ + -Dorg.gradle.caching="${CCACHE}" \ + -Dorg.gradle.parallel="${CCACHE}" \ + -Dorg.gradle.workers.max="${NUM_JOBS}" \ + -Pandroid.injected.build.abi="${ABI}" \ + -PYUZU_ANDROID_ARGS="$*" -if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then +echo "-- packaging AAB" +cp app/build/outputs/bundle/"${LOWER_FLAVOR}${TYPE}/app-${LOWER_FLAVOR}-${LOWER_TYPE}.aab" \ + "${ARTIFACTS_DIR}/app-${LOWER_FLAVOR}-${ABI}-${LOWER_TYPE}.aab" || echo "AAB not found" + +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}/" \ No newline at end of file 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..7abee41efe 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -8,6 +8,7 @@ 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.bundling.AbstractArchiveTask 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 = @@ -90,12 +86,18 @@ android { *extraCMakeArgs.toTypedArray() ) ) - - abiFilters("arm64-v8a") } } } + splits { + abi { + isEnable = true + reset() + include("arm64-v8a", "x86_64") + } + } + val keystoreFile = System.getenv("ANDROID_KEYSTORE_FILE") signingConfigs { if (keystoreFile != null) { @@ -127,7 +129,7 @@ android { isMinifyEnabled = true isDebuggable = false proguardFiles( - getDefaultProguardFile("proguard-android.txt"), + getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } @@ -139,7 +141,7 @@ android { signingConfig = signingConfigs.getByName("default") isDebuggable = true proguardFiles( - getDefaultProguardFile("proguard-android.txt"), + getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) versionNameSuffix = "-relWithDebInfo" @@ -321,3 +323,30 @@ fun getGitVersion(): String { } return versionName.ifEmpty { "0.0" } } + +// this sucks but it works +afterEvaluate { + android.applicationVariants.all { variant -> + val variantName = variant.name.replaceFirstChar { it.uppercase() } + + val packageTask = tasks.named("package${variantName}") + + val copyTask = tasks.register("copy${variantName}Output") { + val outDir = file("../../../artifacts") + into(outDir) + doFirst { outDir.mkdirs() } + + from(packageTask.map { it.outputs.files }) { + include("*.apk") + } + + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } + + tasks.named("assemble${variantName}").configure { + finalizedBy(copyTask) + } + + true + } +}