[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 <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3086
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
This commit is contained in:
crueter
2025-12-03 06:40:11 +01:00
parent 33ee9de85a
commit fe8017734b
11 changed files with 253 additions and 75 deletions

View File

@@ -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 <<EOF
Usage: $0 [-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:
-r, --release Enable update checker. If set, sets the DEVEL bool variable to false.
By default, DEVEL is true.
-t, --target <FLAVOR> Build flavor (variable: TARGET)
Valid values are: legacy, optimized, standard, chromeos
Default: standard
-b, --build-type <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}/"

View File

@@ -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}/"

View File

@@ -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()

View File

@@ -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)

View File

@@ -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": {

62
docs/build/Android.md vendored
View File

@@ -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\<user-name>\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 <FLAVOR> Build flavor (variable: TARGET)
Valid values are: legacy, optimized, standard
Default: standard
-b, --build-type <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
<https://developer.android.com/studio/intro>

View File

@@ -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": {

View File

@@ -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

View File

@@ -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"
}
}

View File

@@ -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<Copy>(taskName) {
group = "publishing"
description = "Copy APK and AAB for $variantName to $artifactsDir"
from(apkFile)
from(aabFile)
into(artifactsDir)
dependsOn("assemble${variantTask}")
dependsOn("bundle${variantTask}")
}
}
}

View File

@@ -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