[cmake, frontend] Add nightly build modifier (#3431)

The `NIGHTLY_BUILD` option changes the app name to "Eden Nightly" and
changes the auto-update URL to use our new Nightly repository.

This needs added to Android, but I can't right now as I have to leave.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3431
This commit is contained in:
crueter
2026-01-31 01:18:36 +01:00
parent df838a57fd
commit 638663b28e
12 changed files with 160 additions and 50 deletions

View File

@@ -1,6 +1,6 @@
#!/bin/sh -e #!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project # SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
NUM_JOBS=$(nproc 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || echo 2) NUM_JOBS=$(nproc 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || echo 2)
@@ -29,6 +29,7 @@ Options:
-b, --build-type <TYPE> Build type (variable: TYPE) -b, --build-type <TYPE> Build type (variable: TYPE)
Valid values are: Release, RelWithDebInfo, Debug Valid values are: Release, RelWithDebInfo, Debug
Default: Debug Default: Debug
-n, --nightly Create a nightly build.
Extra arguments are passed to CMake (e.g. -DCMAKE_OPTION_NAME=VALUE) Extra arguments are passed to CMake (e.g. -DCMAKE_OPTION_NAME=VALUE)
Set the CCACHE variable to "true" to enable build caching. Set the CCACHE variable to "true" to enable build caching.
@@ -61,6 +62,7 @@ while true; do
-r|--release) DEVEL=false ;; -r|--release) DEVEL=false ;;
-t|--target) target "$2"; shift ;; -t|--target) target "$2"; shift ;;
-b|--build-type) type "$2"; shift ;; -b|--build-type) type "$2"; shift ;;
-n|--nightly) NIGHTLY=true ;;
-h|--help) usage ;; -h|--help) usage ;;
*) break ;; *) break ;;
esac esac
@@ -101,7 +103,20 @@ cd src/android
chmod +x ./gradlew chmod +x ./gradlew
set -- "$@" -DUSE_CCACHE="${CCACHE}" set -- "$@" -DUSE_CCACHE="${CCACHE}"
[ "$DEVEL" != "true" ] && set -- "$@" -DENABLE_UPDATE_CHECKER=ON
nightly() {
[ "$NIGHTLY" = "true" ]
}
if nightly || [ "$DEVEL" != "true" ]; then
set -- "$@" -DENABLE_UPDATE_CHECKER=ON
fi
if nightly; then
NIGHTLY=true
else
NIGHTLY=false
fi
echo "-- building..." echo "-- building..."
@@ -110,6 +125,7 @@ echo "-- building..."
-Dorg.gradle.parallel="${CCACHE}" \ -Dorg.gradle.parallel="${CCACHE}" \
-Dorg.gradle.workers.max="${NUM_JOBS}" \ -Dorg.gradle.workers.max="${NUM_JOBS}" \
-PYUZU_ANDROID_ARGS="$*" \ -PYUZU_ANDROID_ARGS="$*" \
-Pnightly="$NIGHTLY" \
--info --info
if [ -n "${ANDROID_KEYSTORE_B64}" ]; then if [ -n "${ANDROID_KEYSTORE_B64}" ]; then

View File

@@ -227,6 +227,8 @@ option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android"
option(YUZU_LEGACY "Apply patches that improve compatibility with older GPUs (e.g. Snapdragon 865) at the cost of performance" OFF) option(YUZU_LEGACY "Apply patches that improve compatibility with older GPUs (e.g. Snapdragon 865) at the cost of performance" OFF)
option(NIGHTLY_BUILD "Use Nightly qualifiers in the update checker and build metadata" OFF)
cmake_dependent_option(YUZU_ROOM "Enable dedicated room functionality" ON "NOT ANDROID" OFF) cmake_dependent_option(YUZU_ROOM "Enable dedicated room functionality" ON "NOT ANDROID" OFF)
cmake_dependent_option(YUZU_ROOM_STANDALONE "Enable standalone room executable" ON "YUZU_ROOM" OFF) cmake_dependent_option(YUZU_ROOM_STANDALONE "Enable standalone room executable" ON "YUZU_ROOM" OFF)

View File

@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project # SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2019 yuzu Emulator Project # SPDX-FileCopyrightText: 2019 yuzu Emulator Project
@@ -8,8 +8,8 @@
include(GetSCMRev) include(GetSCMRev)
function(get_timestamp _var) function(get_timestamp _var)
string(TIMESTAMP timestamp UTC) string(TIMESTAMP timestamp UTC)
set(${_var} "${timestamp}" PARENT_SCOPE) set(${_var} "${timestamp}" PARENT_SCOPE)
endfunction() endfunction()
get_timestamp(BUILD_DATE) get_timestamp(BUILD_DATE)
@@ -28,14 +28,21 @@ set(GIT_DESC ${BUILD_VERSION})
# Generate cpp with Git revision from template # Generate cpp with Git revision from template
# Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well # Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well
set(REPO_NAME "Eden")
set(BUILD_ID ${GIT_REFSPEC})
set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ")
set(CXX_COMPILER "${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")
# Auto-updater metadata! Must somewhat mirror GitHub API endpoint # Auto-updater metadata! Must somewhat mirror GitHub API endpoint
set(BUILD_AUTO_UPDATE_WEBSITE "https://github.com") set(BUILD_AUTO_UPDATE_WEBSITE "https://github.com")
set(BUILD_AUTO_UPDATE_API "http://api.github.com") set(BUILD_AUTO_UPDATE_API "http://api.github.com")
set(BUILD_AUTO_UPDATE_REPO "eden-emulator/Releases")
if (NIGHTLY_BUILD)
set(BUILD_AUTO_UPDATE_REPO "Eden-CI/Nightly")
set(REPO_NAME "Eden Nightly")
else()
set(BUILD_AUTO_UPDATE_REPO "eden-emulator/Releases")
set(REPO_NAME "Eden")
endif()
set(BUILD_ID ${GIT_REFSPEC})
set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ")
set(CXX_COMPILER "${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")
configure_file(scm_rev.cpp.in scm_rev.cpp @ONLY) configure_file(scm_rev.cpp.in scm_rev.cpp @ONLY)

View File

@@ -20,6 +20,10 @@ if (YUZU_STATIC_BUILD)
add_compile_definitions(QT_STATICPLUGIN) add_compile_definitions(QT_STATICPLUGIN)
endif() endif()
if (NIGHTLY_BUILD)
add_compile_definitions(NIGHTLY_BUILD)
endif()
# Set compilation flags # Set compilation flags
if (MSVC AND NOT CXX_CLANG) if (MSVC AND NOT CXX_CLANG)
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE) set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)

View File

@@ -5,6 +5,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
// import android.annotation.SuppressLint // import android.annotation.SuppressLint
import com.android.build.gradle.api.ApplicationVariant
import kotlin.collections.setOf import kotlin.collections.setOf
import org.jlleitschuh.gradle.ktlint.reporter.ReporterType import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
import com.github.triplet.gradle.androidpublisher.ReleaseStatus import com.github.triplet.gradle.androidpublisher.ReleaseStatus
@@ -37,6 +38,9 @@ android {
compileSdkVersion = "android-36" compileSdkVersion = "android-36"
ndkVersion = "28.2.13676358" ndkVersion = "28.2.13676358"
val isNightly =
providers.gradleProperty("nightly").orNull?.toBooleanStrictOrNull() ?: false
buildFeatures { buildFeatures {
viewBinding = true viewBinding = true
} }
@@ -71,6 +75,7 @@ android {
val extraCMakeArgs = val extraCMakeArgs =
(project.findProperty("YUZU_ANDROID_ARGS") as String?)?.split("\\s+".toRegex()) (project.findProperty("YUZU_ANDROID_ARGS") as String?)?.split("\\s+".toRegex())
?: emptyList() ?: emptyList()
arguments.addAll( arguments.addAll(
listOf( listOf(
"-DENABLE_QT=0", // Don't use QT "-DENABLE_QT=0", // Don't use QT
@@ -89,6 +94,13 @@ android {
) )
) )
if (isNightly) {
arguments.addAll(listOf(
"-DENABLE_UPDATE_CHECKER=ON",
"-DNIGHTLY_BUILD=ON",
))
}
abiFilters("arm64-v8a") abiFilters("arm64-v8a")
} }
} }
@@ -125,7 +137,12 @@ android {
signingConfigs.getByName("default") signingConfigs.getByName("default")
} }
manifestPlaceholders += mapOf("appNameSuffix" to "") if (isNightly) {
applicationIdSuffix = ".nightly"
manifestPlaceholders += mapOf("appNameSuffix" to " Nightly")
} else {
manifestPlaceholders += mapOf("appNameSuffix" to "")
}
isMinifyEnabled = true isMinifyEnabled = true
isDebuggable = false isDebuggable = false
@@ -239,6 +256,15 @@ android {
path = file("${edenDir}/CMakeLists.txt") path = file("${edenDir}/CMakeLists.txt")
} }
} }
productFlavors.all {
val currentName = manifestPlaceholders["appNameBase"] as? String ?: "Eden"
val suffix = if (isNightly) " Nightly" else ""
// apply nightly suffix I/A
resValue("string", "app_name_suffixed", "$currentName$suffix")
resValue("string", "app_name", "Eden$suffix")
}
} }
idea { idea {
@@ -258,7 +284,7 @@ tasks.register<Delete>("ktlintReset", fun Delete.() {
val showFormatHelp = { val showFormatHelp = {
logger.lifecycle( logger.lifecycle(
"If this check fails, please try running \"gradlew ktlintFormat\" for automatic " + "If this check fails, please try running \"gradlew ktlintFormat\" for automatic " +
"codestyle fixes" "codestyle fixes"
) )
} }
tasks.getByPath("ktlintKotlinScriptCheck").doFirst { showFormatHelp.invoke() } tasks.getByPath("ktlintKotlinScriptCheck").doFirst { showFormatHelp.invoke() }

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
@@ -218,7 +218,7 @@ object NativeLibrary {
/** /**
* Checks for available updates. * Checks for available updates.
*/ */
external fun checkForUpdate(): String? external fun checkForUpdate(): Array<String>?
/** /**
* Return the URL to the release page * Return the URL to the release page
@@ -228,13 +228,18 @@ object NativeLibrary {
/** /**
* Return the URL to download the APK for the given version * Return the URL to download the APK for the given version
*/ */
external fun getUpdateApkUrl(version: String, packageId: String): String external fun getUpdateApkUrl(tag: String, artifact: String, packageId: String): String
/** /**
* Returns whether the update checker is enabled through CMAKE options. * Returns whether the update checker is enabled through CMAKE options.
*/ */
external fun isUpdateCheckerEnabled(): Boolean external fun isUpdateCheckerEnabled(): Boolean
/**
* Returns whether or not this is a nightly build.
*/
external fun isNightlyBuild(): Boolean
/** /**
* Returns the build version generated by CMake (BUILD_VERSION). * Returns the build version generated by CMake (BUILD_VERSION).
*/ */

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.ui.main package org.yuzu.yuzu_emu.ui.main
@@ -183,18 +183,26 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val latestVersion = NativeLibrary.checkForUpdate() val latestVersion = NativeLibrary.checkForUpdate()
if (latestVersion != null) { if (latestVersion != null) {
runOnUiThread { runOnUiThread {
showUpdateDialog(latestVersion) val tag: String = latestVersion[0]
val name: String = latestVersion[1]
showUpdateDialog(tag, name)
} }
} }
}.start() }.start()
} }
private fun showUpdateDialog(version: String) { private fun showUpdateDialog(tag: String, name: String) {
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setTitle(R.string.update_available) .setTitle(R.string.update_available)
.setMessage(getString(R.string.update_available_description, version)) .setMessage(getString(R.string.update_available_description, name))
.setPositiveButton(android.R.string.ok) { _, _ -> .setPositiveButton(android.R.string.ok) { _, _ ->
downloadAndInstallUpdate(version) var artifact = tag
// Nightly builds have a slightly different format
if (NativeLibrary.isNightlyBuild()) {
val splitTag = tag.split('.')
artifact = splitTag.subList(1, splitTag.size - 1).joinToString(".")
}
downloadAndInstallUpdate(tag, artifact)
} }
.setNeutralButton(R.string.cancel) { dialog, _ -> .setNeutralButton(R.string.cancel) { dialog, _ ->
dialog.dismiss() dialog.dismiss()
@@ -207,11 +215,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
.show() .show()
} }
private fun downloadAndInstallUpdate(version: String) { private fun downloadAndInstallUpdate(version: String, artifact: String) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val packageId = applicationContext.packageName val packageId = applicationContext.packageName
val apkUrl = NativeLibrary.getUpdateApkUrl(version, packageId) val apkUrl = NativeLibrary.getUpdateApkUrl(version, artifact, packageId)
val apkFile = File(cacheDir, "update-$version.apk") val apkFile = File(cacheDir, "update-$artifact.apk")
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
showDownloadProgressDialog() showDownloadProgressDialog()

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -1595,7 +1595,6 @@ JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_updatePowerState(
g_has_battery.store(hasBattery, std::memory_order_relaxed); g_has_battery.store(hasBattery, std::memory_order_relaxed);
} }
// return #ifdef ENABLE_UPDATE_CHECKER
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isUpdateCheckerEnabled( JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isUpdateCheckerEnabled(
JNIEnv* env, JNIEnv* env,
jobject obj) { jobject obj) {
@@ -1606,22 +1605,47 @@ JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isUpdateChecker
#endif #endif
} }
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isNightlyBuild(
JNIEnv* env,
jobject obj) {
#ifdef NIGHTLY_BUILD
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
#ifdef ENABLE_UPDATE_CHECKER #ifdef ENABLE_UPDATE_CHECKER
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_checkForUpdate( JNIEXPORT jobjectArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_checkForUpdate(
JNIEnv* env, JNIEnv* env,
jobject obj) { jobject obj) {
const bool is_prerelease = ((strstr(Common::g_build_version, "pre-alpha") != nullptr) || const bool is_prerelease = ((strstr(Common::g_build_version, "pre-alpha") != nullptr) ||
(strstr(Common::g_build_version, "alpha") != nullptr) || (strstr(Common::g_build_version, "alpha") != nullptr) ||
(strstr(Common::g_build_version, "beta") != nullptr) || (strstr(Common::g_build_version, "beta") != nullptr) ||
(strstr(Common::g_build_version, "rc") != nullptr)); (strstr(Common::g_build_version, "rc") != nullptr));
const std::optional<std::string> latest_release_tag = const std::optional<UpdateChecker::Update> release =
UpdateChecker::GetLatestRelease(is_prerelease); UpdateChecker::GetLatestRelease(is_prerelease);
if (latest_release_tag && latest_release_tag.value() != Common::g_build_version) { if (!release || release->tag == Common::g_build_version) {
return env->NewStringUTF(latest_release_tag.value().c_str()); return nullptr;
} }
return nullptr;
const std::string tag = release->tag;
const std::string name = release->name;
jobjectArray result = env->NewObjectArray(2, env->FindClass("java/lang/String"), nullptr);
const jstring jtag = env->NewStringUTF(tag.c_str());
const jstring jname = env->NewStringUTF(name.c_str());
env->SetObjectArrayElement(result, 0, jtag);
env->SetObjectArrayElement(result, 1, jname);
env->DeleteLocalRef(jtag);
env->DeleteLocalRef(jname);
return result;
} }
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateUrl( JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateUrl(
@@ -1640,9 +1664,11 @@ JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateUrl(
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateApkUrl( JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateApkUrl(
JNIEnv* env, JNIEnv* env,
jobject obj, jobject obj,
jstring version, jstring tag,
jstring artifact,
jstring packageId) { jstring packageId) {
const char* version_str = env->GetStringUTFChars(version, nullptr); const char* version_str = env->GetStringUTFChars(tag, nullptr);
const char* artifact_str = env->GetStringUTFChars(artifact, nullptr);
const char* package_id_str = env->GetStringUTFChars(packageId, nullptr); const char* package_id_str = env->GetStringUTFChars(packageId, nullptr);
std::string variant; std::string variant;
@@ -1653,7 +1679,11 @@ JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateApkUrl(
} else if (package_id.find("com.miHoYo.Yuanshen") != std::string::npos) { } else if (package_id.find("com.miHoYo.Yuanshen") != std::string::npos) {
variant = "optimized"; variant = "optimized";
} else { } else {
#ifdef ARCHITECTURE_arm64
variant = "standard"; variant = "standard";
#else
variant = "chromeos";
#endif
} }
const std::string apk_filename = fmt::format("Eden-Android-{}-{}.apk", version_str, variant); const std::string apk_filename = fmt::format("Eden-Android-{}-{}.apk", version_str, variant);
@@ -1663,7 +1693,7 @@ JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateApkUrl(
version_str, version_str,
apk_filename); apk_filename);
env->ReleaseStringUTFChars(version, version_str); env->ReleaseStringUTFChars(tag, version_str);
env->ReleaseStringUTFChars(packageId, package_id_str); env->ReleaseStringUTFChars(packageId, package_id_str);
return env->NewStringUTF(url.c_str()); return env->NewStringUTF(url.c_str());
} }

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright Citra Emulator Project / Azahar Emulator Project
@@ -78,7 +78,7 @@ std::optional<std::string> UpdateChecker::GetResponse(std::string url, std::stri
} }
} }
std::optional<std::string> UpdateChecker::GetLatestRelease(bool include_prereleases) { std::optional<UpdateChecker::Update> UpdateChecker::GetLatestRelease(bool include_prereleases) {
const auto update_check_url = std::string{Common::g_build_auto_update_api}; const auto update_check_url = std::string{Common::g_build_auto_update_api};
std::string update_check_path = fmt::format("/repos/{}", std::string update_check_path = fmt::format("/repos/{}",
std::string{Common::g_build_auto_update_repo}); std::string{Common::g_build_auto_update_repo});
@@ -96,6 +96,9 @@ std::optional<std::string> UpdateChecker::GetLatestRelease(bool include_prerelea
const std::string latest_tag const std::string latest_tag
= nlohmann::json::parse(tags_response.value()).at(0).at("name"); = nlohmann::json::parse(tags_response.value()).at(0).at("name");
const std::string latest_name =
nlohmann::json::parse(releases_response.value()).at(0).at("name");
const bool latest_tag_has_release = releases_response.value().find( const bool latest_tag_has_release = releases_response.value().find(
fmt::format("\"{}\"", latest_tag)) fmt::format("\"{}\"", latest_tag))
!= std::string::npos; != std::string::npos;
@@ -105,7 +108,7 @@ std::optional<std::string> UpdateChecker::GetLatestRelease(bool include_prerelea
if (!latest_tag_has_release) if (!latest_tag_has_release)
return {}; return {};
return latest_tag; return Update{latest_tag, latest_name};
} else { // This is a stable release, only check for other stable releases. } else { // This is a stable release, only check for other stable releases.
update_check_path += "/releases/latest"; update_check_path += "/releases/latest";
const auto response = UpdateChecker::GetResponse(update_check_url, update_check_path); const auto response = UpdateChecker::GetResponse(update_check_url, update_check_path);
@@ -114,7 +117,9 @@ std::optional<std::string> UpdateChecker::GetLatestRelease(bool include_prerelea
return {}; return {};
const std::string latest_tag = nlohmann::json::parse(response.value()).at("tag_name"); const std::string latest_tag = nlohmann::json::parse(response.value()).at("tag_name");
return latest_tag; const std::string latest_name = nlohmann::json::parse(response.value()).at("name");
return Update{latest_tag, latest_name};
} }
} catch (nlohmann::detail::out_of_range &) { } catch (nlohmann::detail::out_of_range &) {

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright Citra Emulator Project / Azahar Emulator Project
@@ -11,6 +11,12 @@
#include <string> #include <string>
namespace UpdateChecker { namespace UpdateChecker {
typedef struct {
std::string tag;
std::string name;
} Update;
std::optional<std::string> GetResponse(std::string url, std::string path); std::optional<std::string> GetResponse(std::string url, std::string path);
std::optional<std::string> GetLatestRelease(bool include_prereleases); std::optional<Update> GetLatestRelease(bool include_prereleases);
} // namespace UpdateChecker } // namespace UpdateChecker

View File

@@ -516,17 +516,17 @@ MainWindow::MainWindow(bool has_broken_vulkan)
#ifdef ENABLE_UPDATE_CHECKER #ifdef ENABLE_UPDATE_CHECKER
if (UISettings::values.check_for_updates) { if (UISettings::values.check_for_updates) {
update_future = QtConcurrent::run([]() -> QString { update_future = QtConcurrent::run([]() -> UpdateChecker::Update {
const bool is_prerelease = ((strstr(Common::g_build_version, "pre-alpha") != NULL) || const bool is_prerelease = ((strstr(Common::g_build_version, "pre-alpha") != NULL) ||
(strstr(Common::g_build_version, "alpha") != NULL) || (strstr(Common::g_build_version, "alpha") != NULL) ||
(strstr(Common::g_build_version, "beta") != NULL) || (strstr(Common::g_build_version, "beta") != NULL) ||
(strstr(Common::g_build_version, "rc") != NULL)); (strstr(Common::g_build_version, "rc") != NULL));
const std::optional<std::string> latest_release_tag = const std::optional<UpdateChecker::Update> latest_release_tag =
UpdateChecker::GetLatestRelease(is_prerelease); UpdateChecker::GetLatestRelease(is_prerelease);
if (latest_release_tag && latest_release_tag.value() != Common::g_build_version) { if (latest_release_tag && latest_release_tag->tag != Common::g_build_version) {
return QString::fromStdString(latest_release_tag.value()); return latest_release_tag.value();
} }
return QString{}; return UpdateChecker::Update{};
}); });
update_watcher.connect(&update_watcher, &QFutureWatcher<QString>::finished, this, update_watcher.connect(&update_watcher, &QFutureWatcher<QString>::finished, this,
&MainWindow::OnEmulatorUpdateAvailable); &MainWindow::OnEmulatorUpdateAvailable);
@@ -4020,8 +4020,8 @@ void MainWindow::OnCaptureScreenshot() {
#ifdef ENABLE_UPDATE_CHECKER #ifdef ENABLE_UPDATE_CHECKER
void MainWindow::OnEmulatorUpdateAvailable() { void MainWindow::OnEmulatorUpdateAvailable() {
QString version_string = update_future.result(); UpdateChecker::Update version = update_future.result();
if (version_string.isEmpty()) if (version.tag.empty())
return; return;
QMessageBox update_prompt(this); QMessageBox update_prompt(this);
@@ -4030,14 +4030,14 @@ void MainWindow::OnEmulatorUpdateAvailable() {
update_prompt.addButton(QMessageBox::Yes); update_prompt.addButton(QMessageBox::Yes);
update_prompt.addButton(QMessageBox::Ignore); update_prompt.addButton(QMessageBox::Ignore);
update_prompt.setText( update_prompt.setText(
tr("Download the %1 update?").arg(version_string)); tr("Download %1?").arg(version.name));
update_prompt.exec(); update_prompt.exec();
if (update_prompt.button(QMessageBox::Yes) == update_prompt.clickedButton()) { if (update_prompt.button(QMessageBox::Yes) == update_prompt.clickedButton()) {
auto const full_url = fmt::format("{}/{}/releases/tag/", auto const full_url = fmt::format("{}/{}/releases/tag/",
std::string{Common::g_build_auto_update_website}, std::string{Common::g_build_auto_update_website},
std::string{Common::g_build_auto_update_repo} std::string{Common::g_build_auto_update_repo}
); );
QDesktopServices::openUrl(QUrl(QString::fromStdString(full_url) + version_string)); QDesktopServices::openUrl(QUrl(QString::fromStdString(full_url + version.tag)));
} }
} }
#endif #endif

View File

@@ -18,6 +18,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "frontend_common/content_manager.h" #include "frontend_common/content_manager.h"
#include "frontend_common/update_checker.h"
#include "input_common/drivers/tas_input.h" #include "input_common/drivers/tas_input.h"
#include "qt_common/config/qt_config.h" #include "qt_common/config/qt_config.h"
#include "qt_common/util/game.h" #include "qt_common/util/game.h"
@@ -471,8 +472,8 @@ private:
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem; std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
#ifdef ENABLE_UPDATE_CHECKER #ifdef ENABLE_UPDATE_CHECKER
QFuture<QString> update_future; QFuture<UpdateChecker::Update> update_future;
QFutureWatcher<QString> update_watcher; QFutureWatcher<UpdateChecker::Update> update_watcher;
#endif #endif
MultiplayerState* multiplayer_state = nullptr; MultiplayerState* multiplayer_state = nullptr;