Removed sfml-activity which is no longer needed to bootstrap Android applications

This commit is contained in:
Bruno Van de Velde 2023-11-15 14:31:46 +01:00 committed by Chris Thrasher
parent ec50a7332b
commit 025a9ddc53
7 changed files with 5 additions and 261 deletions

View File

@ -96,18 +96,9 @@ if(SFML_OS_ANDROID)
# we install libs in a subdirectory named after the ABI
set(CMAKE_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/${CMAKE_ANDROID_ARCH_ABI}")
# pass shared STL configuration (if any)
if(CMAKE_ANDROID_STL_TYPE MATCHES "_shared")
add_definitions("-DSTL_LIBRARY=${CMAKE_ANDROID_STL_TYPE}")
endif()
# let the user switch ABIs
set(ANDROID_ABI "armeabi-v7a" CACHE STRING "Look at the NDK docs for currently supported ABIs")
# this is a workaround to compile sfml-activity without the stl library as a dependency
# we save the original compilation command line to restore it later in Macro.cmake
set(CMAKE_CXX_CREATE_SHARED_LIBRARY_WITH_STL ${CMAKE_CXX_CREATE_SHARED_LIBRARY})
set(CMAKE_CXX_CREATE_SHARED_LIBRARY_WITHOUT_STL "<CMAKE_CXX_COMPILER> <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>")
endif()
# Install directories

View File

@ -212,18 +212,6 @@ macro(sfml_add_library module)
sfml_set_common_ios_properties(${target})
endif()
# sfml-activity library is our bootstrap activity and must not depend on stlport_shared
# (otherwise Android will fail to load it)
if(SFML_OS_ANDROID)
if(${target} MATCHES "sfml-activity")
set_target_properties(${target} PROPERTIES COMPILE_FLAGS -fpermissive)
set_target_properties(${target} PROPERTIES LINK_FLAGS "-landroid -llog")
set(CMAKE_CXX_CREATE_SHARED_LIBRARY ${CMAKE_CXX_CREATE_SHARED_LIBRARY_WITHOUT_STL})
else()
set(CMAKE_CXX_CREATE_SHARED_LIBRARY ${CMAKE_CXX_CREATE_SHARED_LIBRARY_WITH_STL})
endif()
endif()
# add the install rule
install(TARGETS ${target} EXPORT SFMLConfigExport
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT bin

View File

@ -16,8 +16,7 @@
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="true">
<meta-data android:name="android.app.lib_name" android:value="sfml-activity-d" />
<meta-data android:name="sfml.app.lib_name" android:value="sfml-example" />
<meta-data android:name="android.app.lib_name" android:value="sfml-example" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@ -6,20 +6,14 @@ set(SFML_DIR "${CMAKE_ANDROID_NDK}/sources/third_party/sfml/lib/${CMAKE_ANDROID_
# Create the libsfml-example.so library that contains the application's c++ code
add_library(sfml-example SHARED main.cpp)
# Find and link SFML.
# We must link to the network and audio components even if we don't use them, because sfml-activity will try to load them
# Find and link SFML
find_package(SFML 3 REQUIRED COMPONENTS Graphics Audio Network)
target_link_libraries(sfml-example PRIVATE SFML::Graphics SFML::Audio SFML::Network SFML::Activity android log)
target_link_libraries(sfml-example PRIVATE SFML::Graphics android log)
# The ANativeActivity_onCreate function from SFML::Main needs to be exposed in
# our libsfml-example.so file so that is can be loaded at runtime by sfml-activity.
# our libsfml-example.so file so that is can be loaded at runtime.
target_link_libraries(sfml-example PUBLIC
-Wl,--whole-archive
SFML::Main
-Wl,--no-whole-archive
)
# We need to manually copy the OpenAL library into the apk, even if we don't use it, because sfml-activity will try to load it
add_library(OpenAL::OpenAL SHARED IMPORTED)
set_target_properties(OpenAL::OpenAL PROPERTIES IMPORTED_LOCATION "${CMAKE_ANDROID_NDK}/sources/third_party/sfml/extlibs/lib/${CMAKE_ANDROID_ARCH_ABI}/libopenal.so")
target_link_libraries(sfml-example PRIVATE OpenAL::OpenAL)

View File

@ -42,7 +42,7 @@ set(LIBRARY_OUTPUT_PATH "${PROJECT_BINARY_DIR}/lib")
# sfml-system
add_subdirectory(System)
# sfml-main and sfml-activity
# sfml-main
if(SFML_OS_WINDOWS OR SFML_OS_ANDROID OR SFML_OS_IOS)
add_subdirectory(Main)
endif()

View File

@ -31,10 +31,3 @@ set_target_properties(sfml-main PROPERTIES
RELEASE_POSTFIX ""
MINSIZEREL_POSTFIX ""
RELWITHDEBINFO_POSTFIX "")
# because of a current limitation on Android (which prevents one library
# from depending on shared libraries), we need a bootstrap activity which
# will load our shared libraries manually
if(SFML_OS_ANDROID)
sfml_add_library(Activity SOURCES ${SRCROOT}/SFMLActivity.cpp)
endif()

View File

@ -1,221 +0,0 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2013 Jonathan De Wachter (dewachter.jonathan@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
#include <android/log.h>
#include <android/native_activity.h>
#include <jni.h>
#include <dlfcn.h>
#include <string>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_INFO, "sfml-activity", __VA_ARGS__))
namespace
{
using activityOnCreatePointer = void (*)(ANativeActivity*, void*, std::size_t);
}
const char* getLibraryName(JNIEnv* lJNIEnv, jobject& objectActivityInfo)
{
// This function reads the value of meta-data "sfml.app.lib_name"
// found in the Android Manifest file and returns it. It performs the
// following Java code using the JNI interface:
//
// ai.metaData.getString("sfml.app.lib_name");
static char name[256];
// Get metaData instance from the ActivityInfo object
jclass classActivityInfo = lJNIEnv->FindClass("android/content/pm/ActivityInfo");
jfieldID fieldMetaData = lJNIEnv->GetFieldID(classActivityInfo, "metaData", "Landroid/os/Bundle;");
jobject objectMetaData = lJNIEnv->GetObjectField(objectActivityInfo, fieldMetaData);
// Create a java string object containing "sfml.app.lib_name"
jobject objectName = lJNIEnv->NewStringUTF("sfml.app.lib_name");
// Get the value of meta-data named "sfml.app.lib_name"
jclass classBundle = lJNIEnv->FindClass("android/os/Bundle");
jmethodID methodGetString = lJNIEnv->GetMethodID(classBundle, "getString", "(Ljava/lang/String;)Ljava/lang/String;");
auto* valueString = static_cast<jstring>(lJNIEnv->CallObjectMethod(objectMetaData, methodGetString, objectName));
// No meta-data "sfml.app.lib_name" was found so we abort and inform the user
if (valueString == nullptr)
{
LOGE("No meta-data 'sfml.app.lib_name' found in AndroidManifest.xml file");
std::exit(1);
}
// Convert the application name to a C++ string and return it
const auto applicationNameLength = static_cast<std::size_t>(lJNIEnv->GetStringUTFLength(valueString));
const char* applicationName = lJNIEnv->GetStringUTFChars(valueString, nullptr);
if (applicationNameLength >= 256)
{
LOGE("The value of 'sfml.app.lib_name' must not be longer than 255 characters.");
std::exit(1);
}
strncpy(name, applicationName, static_cast<std::size_t>(applicationNameLength));
name[applicationNameLength] = '\0';
lJNIEnv->ReleaseStringUTFChars(valueString, applicationName);
return name;
}
void* loadLibrary(const char* libraryName, JNIEnv* lJNIEnv, jobject& objectActivityInfo)
{
// Find out the absolute path of the library
jclass classActivityInfo = lJNIEnv->FindClass("android/content/pm/ActivityInfo");
jfieldID fieldApplicationInfo = lJNIEnv->GetFieldID(classActivityInfo,
"applicationInfo",
"Landroid/content/pm/ApplicationInfo;");
jobject objectApplicationInfo = lJNIEnv->GetObjectField(objectActivityInfo, fieldApplicationInfo);
jclass classApplicationInfo = lJNIEnv->FindClass("android/content/pm/ApplicationInfo");
jfieldID fieldNativeLibraryDir = lJNIEnv->GetFieldID(classApplicationInfo, "nativeLibraryDir", "Ljava/lang/String;");
jobject objectDirPath = lJNIEnv->GetObjectField(objectApplicationInfo, fieldNativeLibraryDir);
jclass classSystem = lJNIEnv->FindClass("java/lang/System");
jmethodID staticMethodMapLibraryName = lJNIEnv->GetStaticMethodID(classSystem,
"mapLibraryName",
"(Ljava/lang/String;)Ljava/lang/String;");
jstring libNameObject = lJNIEnv->NewStringUTF(libraryName);
jobject objectName = lJNIEnv->CallStaticObjectMethod(classSystem, staticMethodMapLibraryName, libNameObject);
jclass classFile = lJNIEnv->FindClass("java/io/File");
jmethodID fileConstructor = lJNIEnv->GetMethodID(classFile, "<init>", "(Ljava/lang/String;Ljava/lang/String;)V");
jobject objectFile = lJNIEnv->NewObject(classFile, fileConstructor, objectDirPath, objectName);
// Get the library absolute path and convert it
jmethodID methodGetPath = lJNIEnv->GetMethodID(classFile, "getPath", "()Ljava/lang/String;");
auto* javaLibraryPath = static_cast<jstring>(lJNIEnv->CallObjectMethod(objectFile, methodGetPath));
const char* libraryPath = lJNIEnv->GetStringUTFChars(javaLibraryPath, nullptr);
// Manually load the library
void* handle = dlopen(libraryPath, RTLD_NOW | RTLD_GLOBAL);
if (!handle)
{
LOGE("dlopen(\"%s\"): %s", libraryPath, dlerror());
std::exit(1);
}
// Release the Java string
lJNIEnv->ReleaseStringUTFChars(javaLibraryPath, libraryPath);
return handle;
}
JNIEXPORT void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, std::size_t savedStateSize)
{
// Before we can load a library, we need to find out its location. As
// we're powerless here in C/C++, we need the JNI interface to communicate
// with the attached Java virtual machine and perform some Java calls in
// order to retrieve the absolute path of our libraries.
//
// Here's the snippet of Java code it performs:
// --------------------------------------------
// ai = getPackageManager().getActivityInfo(getIntent().getComponent(), PackageManager.GET_META_DATA);
// File libraryFile = new File(ai.applicationInfo.nativeLibraryDir, System.mapLibraryName(libname));
// String path = libraryFile.getPath();
//
// With libname being the library name such as "jpeg".
// Retrieve JNI environment and JVM instance
JNIEnv* lJNIEnv = activity->env;
// Retrieve the NativeActivity
jobject objectNativeActivity = activity->clazz;
jclass classNativeActivity = lJNIEnv->GetObjectClass(objectNativeActivity);
// Retrieve the ActivityInfo
jmethodID methodGetPackageManager = lJNIEnv->GetMethodID(classNativeActivity,
"getPackageManager",
"()Landroid/content/pm/PackageManager;");
jobject objectPackageManager = lJNIEnv->CallObjectMethod(objectNativeActivity, methodGetPackageManager);
jmethodID methodGetIndent = lJNIEnv->GetMethodID(classNativeActivity, "getIntent", "()Landroid/content/Intent;");
jobject objectIntent = lJNIEnv->CallObjectMethod(objectNativeActivity, methodGetIndent);
jclass classIntent = lJNIEnv->FindClass("android/content/Intent");
jmethodID methodGetComponent = lJNIEnv->GetMethodID(classIntent,
"getComponent",
"()Landroid/content/ComponentName;");
jobject objectComponentName = lJNIEnv->CallObjectMethod(objectIntent, methodGetComponent);
jclass classPackageManager = lJNIEnv->FindClass("android/content/pm/PackageManager");
jfieldID fieldGetMetaData = lJNIEnv->GetStaticFieldID(classPackageManager, "GET_META_DATA", "I");
jint getMetaData = lJNIEnv->GetStaticIntField(classPackageManager, fieldGetMetaData);
jmethodID methodGetActivityInfo = lJNIEnv->GetMethodID(classPackageManager,
"getActivityInfo",
"(Landroid/content/ComponentName;I)Landroid/content/pm/"
"ActivityInfo;");
jobject objectActivityInfo = lJNIEnv->CallObjectMethod(objectPackageManager, methodGetActivityInfo, objectComponentName, getMetaData);
// Load our libraries in reverse order
#if defined(STL_LIBRARY)
#define SFML_QS(s) SFML_S(s)
#define SFML_S(s) #s
loadLibrary(SFML_QS(STL_LIBRARY), lJNIEnv, objectActivityInfo);
#undef SFML_S
#undef SFML_QS
#endif
loadLibrary("openal", lJNIEnv, objectActivityInfo);
#if !defined(SFML_DEBUG)
loadLibrary("sfml-system", lJNIEnv, objectActivityInfo);
loadLibrary("sfml-window", lJNIEnv, objectActivityInfo);
loadLibrary("sfml-graphics", lJNIEnv, objectActivityInfo);
loadLibrary("sfml-audio", lJNIEnv, objectActivityInfo);
loadLibrary("sfml-network", lJNIEnv, objectActivityInfo);
#else
loadLibrary("sfml-system-d", lJNIEnv, objectActivityInfo);
loadLibrary("sfml-window-d", lJNIEnv, objectActivityInfo);
loadLibrary("sfml-graphics-d", lJNIEnv, objectActivityInfo);
loadLibrary("sfml-audio-d", lJNIEnv, objectActivityInfo);
loadLibrary("sfml-network-d", lJNIEnv, objectActivityInfo);
#endif
void* handle = loadLibrary(getLibraryName(lJNIEnv, objectActivityInfo), lJNIEnv, objectActivityInfo);
// Call the original ANativeActivity_onCreate function
auto nativeActivityOnCreate = reinterpret_cast<activityOnCreatePointer>(dlsym(handle, "ANativeActivity_onCreate"));
if (!nativeActivityOnCreate)
{
LOGE("sfml-activity: Undefined symbol ANativeActivity_onCreate");
std::exit(1);
}
nativeActivityOnCreate(activity, savedState, savedStateSize);
}