Build for Android, webOS, Dingux, RetroFW and Miyoo (#65)

* Link the core with -fPIC

The shared library is compiled with -fpic, and the link line carries neither
that nor anything else from CFLAGS. On x86 nothing notices. On MIPS the small
PIC model runs out of GOT addressing range, and because LTO recompiles at link
time the flag has to be on that command too, so the link ended in

  relocation R_MIPS_HI16 against `__gnu_local_gp' cannot be used when making
  a shared object; recompile with -fPIC

-fPIC on both sides costs a little code size on the architectures that draw a
distinction and settles it everywhere.

* libretro: take the CPU from the compiler, not from uname

A cross build runs on an x86_64 host, so uname says x86_64 and the core was
configured for the x86 JIT no matter what the compiler in $(CC) actually
targets. That is why the webOS, dingux, miyoo and RetroFW jobs the other Mega
Drive cores carry are missing here: they could not have worked.

$(CC) -dumpmachine names the real target. Everything that is not x86 selects
the generated 68K and Z80 cores, the same interpreter build the linux-aarch64
job has been publishing all along. A native build is unaffected - there the
compiler and the machine agree.

* ci: build for webOS, Dingux, RetroFW and Miyoo

The templates for these are all cross compiles, which is what the two commits
before this one make possible. This is the rest of the matrix genesis-plus-gx
and picodrive carry, minus the consoles.

* Keep the standalone Android app's JNI out of the core

util.c guards the Storage Access Framework helpers with __ANDROID__ and
!IS_LIB - they call into com/retrodev/blastem/BlastEmActivity through JNI,
which only the standalone app has. Everywhere else that half of the pair was
missing, so an Android build of the core reached for the app's Java and for
SDL, which it does not link:

  system.c:571: call to undeclared function 'open_uri'
  util.c:17: fatal error: 'SDL_system.h' file not found

The rest of the Android-only code - the log wrappers, get_external_storage_
path(), getRomPath, the SDL internal-storage config and userdata dirs, the
content:// directory listing - is the app's too. A core takes its paths from
the frontend, so it gets the same guard and falls through to the code every
other platform uses. The standalone build does not define IS_LIB and is
untouched.

* ci: build for Android

Through android-make rather than a jni/ directory: the 68K and Z80 cores are
generated by cpu_dsl.py while the build runs, which ndk-build has no good way
to do, and plain make keeps that step where it already works.

The JITs want RWX memory, which is not something to count on under Android, so
every ABI takes the generated cores - the same interpreter build aarch64 has
been publishing all along.
This commit is contained in:
WizzardSK authored and GitHub committed 2026-09-01 19:00:16 -05:00
1 parent c4aa40700b
commit 2fbf253b65
7 files changed
+145 -16

No files matched your search

+83 -1
View File
@@ -42,10 +42,24 @@ include:
file: '/osx-arm64.yml'
################################## CELLULAR ################################
#
# Android
- project: 'libretro-infrastructure/ci-templates'
file: '/android-make.yml'
################################## CONSOLES ################################
#
#################################### MISC ##################################
# webOS
- project: 'libretro-infrastructure/ci-templates'
file: '/webos-make.yml'
# Dingux/OpenDingux (MIPS32)
- project: 'libretro-infrastructure/ci-templates'
file: '/dingux-mips32.yml'
# Miyoo (ARM32)
- project: 'libretro-infrastructure/ci-templates'
file: '/dingux-arm32.yml'
# Stages for building
stages:
@@ -99,3 +113,71 @@ libretro-build-osx-arm64:
extends:
- .libretro-osx-arm64-make-default
- .core-defs
################################### CELLULAR #################################
# Android ARMv7a
android-armeabi-v7a:
extends:
- .libretro-android-make-armeabi-v7a
- .core-defs
# Android ARMv8a
android-arm64-v8a:
extends:
- .libretro-android-make-arm64-v8a
- .core-defs
# Android 32-bit x86
android-x86:
extends:
- .libretro-android-make-x86
- .core-defs
# Android 64-bit x86
android-x86_64:
extends:
- .libretro-android-make-x86_64
- .core-defs
##################################### MISC ###################################
# webOS ARMv7a
libretro-build-webos-armv7a:
extends:
- .libretro-webos-armv7a-make-default
- .core-defs
# webOS aarch64
libretro-build-webos-aarch64:
extends:
- .libretro-webos-aarch64-make-default
- .core-defs
# Dingux (MIPS32)
libretro-build-dingux-mips32:
extends:
- .libretro-dingux-mips32-make-default
- .core-defs
# OpenDingux beta (MIPS32)
libretro-build-dingux-odbeta-mips32:
extends:
- .libretro-dingux-odbeta-mips32-make-default
- .core-defs
# RS-90 OpenDingux beta (MIPS32)
libretro-build-rs90-odbeta-mips32:
extends:
- .libretro-rs90-odbeta-mips32-make-default
- .core-defs
# RetroFW (MIPS32)
libretro-build-retrofw-mips32:
extends:
- .libretro-retrofw-mips32-make-default
- .core-defs
# Miyoo (ARM32)
libretro-build-miyoo-arm32:
extends:
- .libretro-miyoo-arm32-make-default
- .core-defs
+5 -2
View File
@@ -355,7 +355,10 @@ SH2DISOBJS:=sh2dis.o sh2_decode.o disasm.o tern.o util.o backend.o
#zlib is left enabled here: LIBOBJS already links $(LIBZOBJS), so this costs no
#extra objects and makes romopen() a gzopen() that transparently handles gzip
LIBCFLAGS=$(CFLAGS) -fpic -DIS_LIB -DDISABLE_NUKLEAR
#-fPIC rather than -fpic: MIPS runs out of GOT addressing range with the small
#model, and with LTO the link step recompiles, so the flag has to be on the link
#line too or the relocations come back as R_MIPS_HI16 against __gnu_local_gp.
LIBCFLAGS=$(CFLAGS) -fPIC -DIS_LIB -DDISABLE_NUKLEAR
all : $(ALL)
@@ -409,7 +412,7 @@ $(LIBOBJDIR) :
mkdir -p $(LIBOBJDIR)/lzma
libblastem.$(SO) : $(LIBOBJS:%.o=$(LIBOBJDIR)/%.o)
$(CC) -shared -o $@ $^ $(LDFLAGS)
$(CC) -shared -fPIC -o $@ $^ $(LDFLAGS)
blastem$(EXE) : $(MAINOBJS:%.o=$(OBJDIR)/%.o)
$(CC) -o $@ $^ $(LDFLAGS) $(PROFFLAGS)
+42 -1
View File
@@ -9,6 +9,30 @@ ifeq ($(platform), emscripten)
ABI := x86_64
target = libblastem.so
libname = blastem_libretro_emscripten.bc
else ifneq (,$(findstring android,$(platform)))
#Android, built the way libretro-infrastructure's android-make jobs drive it:
#plain make with an NDK clang, one ABI per invocation. The 68K and Z80 JITs
#want RWX memory, which is not something to count on under Android, so these
#builds take the generated cores on every ABI - the same ones aarch64 gets.
OS :=Linux
ANDROID_API ?= 21
NDK_LLVM ?= $(ANDROID_NDK_LLVM)
ifeq ($(platform),android-arm64)
ABI := aarch64
CC = $(NDK_LLVM)/bin/aarch64-linux-android$(ANDROID_API)-clang
else ifeq ($(platform),android-x86_64)
ABI := x86_64
CC = $(NDK_LLVM)/bin/x86_64-linux-android$(ANDROID_API)-clang
else ifeq ($(platform),android-x86)
ABI := i686
CC = $(NDK_LLVM)/bin/i686-linux-android$(ANDROID_API)-clang
else
ABI := arm
CC = $(NDK_LLVM)/bin/armv7a-linux-androideabi$(ANDROID_API)-clang
endif
CORE_ARGS := NEW_CORE=1
target = libblastem.so
libname = blastem_libretro_android.so
else ifeq ($(MSYSTEM),MINGW64)
platform = win
OS :=Windows
@@ -58,8 +82,25 @@ else
platform = linux
OS :=Linux
CC ?=gcc
#A cross build (webOS, dingux, miyoo, ...) runs on an x86_64 host with a cross
#compiler, so the CPU to build for is the one the compiler targets rather than
#the one uname reports. Everything that is not x86 picks the generated 68K/Z80
#cores, which is what makes those targets buildable at all.
CC_MACHINE := $(shell $(CC) -dumpmachine 2>/dev/null)
ifeq ($(ARCH),x86)
ABI := i686
else ifneq (,$(findstring aarch64,$(CC_MACHINE)))
ABI := aarch64
else ifneq (,$(findstring arm,$(CC_MACHINE)))
ABI := arm
else ifneq (,$(findstring mips,$(CC_MACHINE)))
ABI := mips
else ifneq (,$(findstring i686,$(CC_MACHINE)))
ABI := i686
else ifneq (,$(findstring i386,$(CC_MACHINE)))
ABI := i686
else ifneq (,$(findstring x86_64,$(CC_MACHINE)))
ABI := x86_64
else ifneq (,$(filter aarch64 arm64,$(UNAMEM)))
ABI := aarch64
else
@@ -77,7 +118,7 @@ ifneq (,$(or $(findstring webos,$(CROSS_COMPILE)),$(findstring starfish,$(CROSS_
endif
core: $(OBJ)
$(MAKE) $(target) OS=$(OS) CC=$(CC) CPU=$(ABI) LIBRETRO=$(LIBRETRO)
$(MAKE) $(target) OS=$(OS) CC=$(CC) CPU=$(ABI) LIBRETRO=$(LIBRETRO) $(CORE_ARGS)
cp -v $(target) $(libname)
install: $(libname)
+1 -1
View File
@@ -30,7 +30,7 @@ typedef vfs_file media_file;
#else
typedef FILE media_file;
#ifdef __ANDROID__
#if defined(__ANDROID__) && !defined(IS_LIB)
FILE* fopen_wrapper(const char *path, const char *mode);
#define media_fopen fopen_wrapper
#else
+5 -5
View File
@@ -13,7 +13,7 @@
#include <unistd.h>
#include <errno.h>
#endif
#ifdef __ANDROID__
#if defined(__ANDROID__) && !defined(IS_LIB)
#include <SDL_system.h>
#include <jni.h>
#endif
@@ -45,7 +45,7 @@ static void persist_path(void)
free(pathfname);
}
#ifdef __ANDROID__
#if defined(__ANDROID__) && !defined(IS_LIB)
static char *get_external_storage_path()
{
static char *ret;
@@ -82,7 +82,7 @@ cleanup:
uint8_t get_initial_browse_path(char **dst)
{
char *base = NULL;
#ifdef __ANDROID__
#if defined(__ANDROID__) && !defined(IS_LIB)
static const char activity_class_name[] = "com/retrodev/blastem/BlastEmActivity";
static const char get_rom_path_name[] = "getRomPath";
JNIEnv *env = SDL_AndroidGetJNIEnv();
@@ -143,7 +143,7 @@ uint8_t get_initial_browse_path(char **dst)
}
#endif
if (!base){
#ifdef __ANDROID__
#if defined(__ANDROID__) && !defined(IS_LIB)
base = get_external_storage_path();
#else
@@ -486,7 +486,7 @@ fallback:
return exe_dir;
}
#ifdef __ANDROID__
#if defined(__ANDROID__) && !defined(IS_LIB)
#include <SDL.h>
char const *get_config_dir()
+6 -3
View File
@@ -32,7 +32,10 @@
#define gzdirect vfs_fdirect
#elif defined(DISABLE_ZLIB)
#define ROMFILE FILE*
#ifdef __ANDROID__
//The Storage Access Framework wrappers belong to the standalone Android app -
//they call into its activity through JNI. A libretro core gets its paths from
//the frontend, so it takes the plain calls, the same way util.c does.
#if defined(__ANDROID__) && !defined(IS_LIB)
#define romopen fopen_wrapper
#else
#define romopen fopen
@@ -44,7 +47,7 @@
#else
#include "zlib/zlib.h"
#define ROMFILE gzFile
#ifdef __ANDROID__
#if defined(__ANDROID__) && !defined(IS_LIB)
#define romopen gzopen_wrapper
gzFile gzopen_wrapper(const char *path, const char *mode);
#else
@@ -563,7 +566,7 @@ end:
#ifndef DISABLE_ZLIB
#ifdef __ANDROID__
#if defined(__ANDROID__) && !defined(IS_LIB)
gzFile gzopen_wrapper(const char *path, const char *mode)
{
if (startswith(path, "content://")) {
+3 -3
View File
@@ -8,7 +8,7 @@
#include <sys/stat.h>
#include <errno.h>
#ifdef __ANDROID__
#if defined(__ANDROID__) && !defined(IS_LIB)
#include <android/log.h>
#include <SDL_system.h>
#include <jni.h>
@@ -901,7 +901,7 @@ char *fgets_timeout(char *dst, size_t size, FILE *f, uint64_t timeout_usec, void
#include <dirent.h>
#ifdef __ANDROID__
#if defined(__ANDROID__) && !defined(IS_LIB)
static dir_entry *jdir_list_helper(JNIEnv *env, jmethodID meth, char *path, size_t *numret)
{
jstring jpath = (*env)->NewStringUTF(env, path);
@@ -940,7 +940,7 @@ static dir_entry *jdir_list_helper(JNIEnv *env, jmethodID meth, char *path, size
dir_entry *get_dir_list(char *path, size_t *numret)
{
#ifdef __ANDROID__
#if defined(__ANDROID__) && !defined(IS_LIB)
debug_message("get_dir_list(%s)\n", path);
if (startswith(path, "content://")) {
static const char activity_class_name[] = "com/retrodev/blastem/BlastEmActivity";