Création d’une application C++ SDL pour iOS sous Linux

Dans cet article, je décrirai la procédure pour créer une application C++ SDL pour iOS sous Linux, signer une archive ipa sans abonnement Apple Developer payant et l’installer sur un appareil propre (iPad) utilisant macOS sans Jailbreak.< /p>

Tout d’abord, installons la chaîne d’outils de build pour Linux :
https://github.com/tpoechtrager/cctools-port

La chaîne d’outils doit être téléchargée depuis le référentiel, puis suivez les instructions sur le site Web Godot Engine pour terminer l’installation :
https://docs.godotengine.org/ru/latest/development/compiling/cross-compiling_for_ios_on_linux.html

Pour le moment, vous devez télécharger Xcode dmg et copier le SDK à partir de là pour créer le port cctools. Cette étape est plus facile à réaliser sur macOS ; copiez simplement les fichiers SDK nécessaires à partir du Xcode installé. Après un assemblage réussi, le terminal contiendra le chemin d’accès à la chaîne d’outils du compilateur croisé.

Vous pouvez ensuite commencer à créer l’application SDL pour iOS. Ouvrons cmake et ajoutons les modifications nécessaires pour construire le code C++ :

SET(CMAKE_SYSTEM_NAME Darwin)
SET(CMAKE_C_COMPILER arm-apple-darwin11-clang)
SET(CMAKE_CXX_COMPILER arm-apple-darwin11-clang++)
SET(CMAKE_LINKER arm-apple-darwin11-ld)

Vous pouvez maintenant compiler en utilisant cmake et make, mais n’oubliez pas d’ajouter $PATH à la chaîne d’outils du compilateur croisé :


PATH=$PATH:~/Sources/cctools-port/usage_examples/ios_toolchain/target/bin

Pour une liaison correcte avec les frameworks et SDL, nous les écrivons en cmake, dépendances du jeu Space Jaguar par exemple :


target_link_libraries(
${FSEGT_PROJECT_NAME}
${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/libclang_rt.ios.a
${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/libSDL2.a
${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/libSDL2_mixer.a
${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/libSDL2_image.a
"${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/CoreServices.framework"
"${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/ImageIO.framework"
"${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/Metal.framework"
"${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/AVFoundation.framework"
"${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/GameController.framework"
"${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/CoreMotion.framework"
"${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/CoreGraphics.framework"
"${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/AudioToolbox.framework"
"${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/CoreAudio.framework"
"${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/QuartzCore.framework"
"${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/OpenGLES.framework"
"${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/UIKit.framework"
"${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/Foundation.framework"
)

Dans mon cas, les bibliothèques SDL, SDL_Image et SDL_mixer sont compilées à l’avance dans Xcode sur macOS pour les liaisons statiques ; Frameworks copiés depuis Xcode. La bibliothèque libclang_rt.ios.a a également été ajoutée, qui inclut des appels d’exécution spécifiques à iOS, par exemple isOSVersionAtLeast. Une macro est incluse pour travailler avec OpenGL ES, désactivant les fonctions non prises en charge dans la version mobile, similaire à Android.

Après avoir résolu tous les problèmes de construction, vous devriez obtenir le binaire assemblé pour arm. Ensuite, envisageons d’exécuter le binaire assemblé sur un appareil sans Jailbreak.

Sur macOS, installez Xcode, inscrivez-vous sur le portail Apple, sans payer pour le programme développeur. Ajouter un compte dans Xcode -> Préférences -> Comptes, créez une application vierge et construisez sur un appareil réel. Lors de l’assemblage, l’appareil sera ajouté au compte développeur gratuit. Après l’assemblage et le lancement, vous devez créer l’archive ; pour ce faire, sélectionnez Appareil et produit iOS génériques -> Archive. Une fois l’archive créée, extrayez-en les fichiersembedded.mobileprovision et PkgInfo. Depuis le journal de build vers l’appareil, recherchez la ligne de codedesign avec la clé de signature correcte, le chemin d’accès au fichier de droits avec l’extension app.xcent, copiez-le.

Copiez le dossier .app de l’archive, remplacez le binaire de l’archive par un compilé par un compilateur croisé sous Linux (par exemple SpaceJaguar.app/SpaceJaguar), puis ajoutez les ressources nécessaires au .app, vérifiez le intégrité des fichiers PkgInfo et Embedded.mobileprovision dans le .app à partir de l’archive, copiez à nouveau si nécessaire. Nous re-signons le .app à l’aide de la commande codesign – le codedesign nécessite une clé d’entrée pour la signature, le chemin d’accès au fichier de droits (peut être renommé avec une extension .plist)

Après la re-signature, créez un dossier Payload, déplacez-y le dossier avec l’extension .app, créez une archive zip avec Payload à la racine, renommez l’archive avec l’extension .ipa. Après cela, dans Xcode, ouvrez la liste des appareils et faites glisser le nouvel ipa vers la liste des applications de l’appareil ; L’installation via Apple Configurator 2 ne fonctionne pas pour cette méthode. Si la re-signature est effectuée correctement, alors l’application avec le nouveau binaire sera installée sur un appareil iOS (par exemple iPad) avec un certificat de 7 jours, cela suffit pour la période de test.

Sources

https://github.com/tpoechtrager/cctools-port
https://docs.godotengine.org/ru/latest/development/compiling/cross-compiling_for_ios_on_linux.html
https://jonnyzzz.com/blog/2018/06/13/link-error-3/
https://stackoverflow.com/questions/6896029/re-sign-ipa-iphone
https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html

Portage d’une application C++ SDL sur Android

Dans cet article, je décrirai mon expérience de portage d’un prototype d’éditeur 3D Cube Art Projectsur Android.
Tout d’abord, regardons le résultat : un éditeur avec un curseur cubique 3D rouge est exécuté dans l’émulateur :

Pour réussir l’assemblage, vous deviez procéder comme suit :

  1. Installez les derniers SDK et NDK Android (plus la version du NDK est récente, mieux c’est).
  2. Téléchargez le code source SDL2, puis prenez le modèle à partir de là pour créer l’application Android.
  3. Ajoutez une image SDL et un mélangeur SDL à l’assemblage.
  4. Ajouter les bibliothèques de mon moteur de jeu et de mon kit d’outils, leurs dépendances (GLM, JSON pour Modern C++)
  5. Adapter les fichiers d’assemblage pour Gradle.
  6. Adapter le code C++ pour la compatibilité avec Android, modifications affectées aux composants dépendants de la plate-forme (OpenGL ES, initialisation du contexte graphique)
  7. Créez et testez le projet sur l’émulateur.

Modèle de projet

Chargement des sources SDL, SDL Image, SDL Mixer :
https://www.libsdl.org/download-2.0.php
Le dossier docs contient des instructions détaillées pour travailler avec le modèle de projet Android ; copiez le répertoire du projet Android dans un dossier séparé, créez un lien symbolique ou copiez le dossier SDL dans Android-project/app/jni.
Nous substituons l’identifiant correct au drapeau avd, lançons l’émulateur Android depuis le répertoire Sdk :

cd ~/Android/Sdk/emulator
./emulator -avd Pixel_2_API_24

Spécifiez les chemins dans le script, assemblez le projet :

rm -rf app/build || true
export ANDROID_HOME=/home/demensdeum/Android/Sdk/
export ANDROID_NDK_HOME=/home/demensdeum/Android/android-ndk-r21-beta2/
./gradlew clean build
./gradlew installDebug

Le modèle de projet SDL avec le code C du fichier doit être assemblé

android-sdl-test-app/cube-art-project-android/app/jni/src/YourSourceHere.c

Dépendances

Téléchargez le code source dans les archives pour SDL_image, SDL_mixer :
https://www.libsdl.org/projects/SDL_image/
https://www.libsdl.org/projects/SDL_mixer/

Chargement des dépendances de votre projet, par exemple mes bibliothèques partagées :
https://gitlab.com/demensdeum/FlameSteelCore/
https://gitlab.com/demensdeum/FlameSteelCommonTraits
https://gitlab.com/demensdeum/FlameSteelBattleHorn
https://gitlab.com/demensdeum/FlameSteelEngineGameToolkit/
https://gitlab.com/demensdeum/FlameSteelEngineGameToolkitFSGL
https://gitlab.com/demensdeum/FSGL
https://gitlab.com/demensdeum/cube-art-project

Nous téléchargeons tout cela dans app/jni, chaque « module » dans un dossier séparé, par exemple app/jni/FSGL. Ensuite, vous avez la possibilité de trouver des générateurs fonctionnels pour les fichiers Application.mk et Android.mk, je ne les ai pas trouvés, mais il existe peut-être une solution simple basée sur CMake. Suivez les liens et commencez à vous familiariser avec le format de fichier d’assemblage pour Android NDK :
https://developer.android.com/ndk/guides/application_mk
https://developer.android.com/ndk/guides/android_mk

Vous devriez également en savoir plus sur les différentes implémentations d’APP_STL dans NDK :
https://developer.android.com/ndk/guides/cpp-support.html

Après familiarisation, nous créons un fichier Android.mk pour chaque « module », suivi d’un exemple de fichier d’assemblage de la bibliothèque partagée Cube-Art-Project :

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

APP_STL := c++_static
APP_CPPFLAGS := -fexceptions
LOCAL_MODULE := CubeArtProject
LOCAL_C_INCLUDES := $(LOCAL_PATH)/src $(LOCAL_PATH)/../include $(LOCAL_PATH)/../include/FlameSteelCommonTraits/src/FlameSteelCommonTraits
LOCAL_EXPORT_C_INCLUDES = $(LOCAL_PATH)/src/

define walk
$(wildcard $(1)) $(foreach e, $(wildcard $(1)/*), $(call walk, $(e)))
endef

ALLFILES = $(call walk, $(LOCAL_PATH)/src)
FILE_LIST := $(filter %.cpp, $(ALLFILES))
$(info CubeArtProject source code files list)
$(info $(FILE_LIST))
LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%)

LOCAL_SHARED_LIBRARIES += FlameSteelCore
LOCAL_SHARED_LIBRARIES += FlameSteelBattleHorn
LOCAL_SHARED_LIBRARIES += FlameSteelCommonTraits
LOCAL_SHARED_LIBRARIES += FlameSteelEngineGameToolkit
LOCAL_SHARED_LIBRARIES += FlameSteelEngineGameToolkitFSGL
LOCAL_SHARED_LIBRARIES += FSGL
LOCAL_SHARED_LIBRARIES += SDL2
LOCAL_SHARED_LIBRARIES += SDL2_image

LOCAL_LDFLAGS := -static-libstdc++
include $(BUILD_SHARED_LIBRARY)

Tout utilisateur expérimenté de CMake comprendra cette configuration dès les premières lignes, les formats sont très similaires, Android.mk n’a pas GLOB_RECURSIVE, vous devez donc rechercher de manière récursive les fichiers sources à l’aide de la fonction walk.

Nous modifions Application.mk, Android.mk pour créer du code C++ et non C :

APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
APP_PLATFORM=android-16
APP_STL := c++_static
APP_CPPFLAGS := -fexceptions

Renommer YourSourceHere.c -> YourSourceHere.cpp, récupérer les entrées, modifier le chemin dans l’assembly, par exemple :

app/jni/src/Android.mk:LOCAL_SRC_FILES := YourSourceHere.cpp

Ensuite, essayez de construire le projet, si vous voyez des erreurs du compilateur concernant l’absence d’en-têtes, puis vérifiez l’exactitude des chemins dans Android.mk ; S’il y a des erreurs de l’éditeur de liens comme « référence non définie », vérifiez que les fichiers de code source dans les assemblys sont correctement spécifiés ; les listes peuvent être tracées en spécifiant $(info $(FILE_LIST)) dans le fichier Android.mk. N’oubliez pas le mécanisme de double liaison, en utilisant des modules dans la clé LOCAL_SHARED_LIBRARIES et en corrigeant la liaison via LD, par exemple pour FSGL :

LOCAL_LDLIBS := -lEGL -lGLESv2

Adaptation et lancement

J’ai dû modifier certaines choses, par exemple supprimer GLEW des versions pour iOS et Android, renommer certains appels OpenGL, ajouter le suffixe EOS (glGenVertexArrays -> glGenVertexArraysOES), inclure une macro pour les fonctions de débogage modernes manquantes , la cerise sur le gâteau est l’inclusion implicite des en-têtes GLES2 indiquant la macro GL_GLEXT_PROTOTYPES 1 :

#define GL_GLEXT_PROTOTYPES 1
#include "SDL_opengles2.h"

J’ai aussi observé un écran noir aux premiers lancements avec une erreur du type “E/libEGL: validate_display:255 error 3008 (EGL_BAD_DISPLAY)”, j’ai changé l’initialisation de la fenêtre SDL, le profil OpenGL et tout a fonctionné :

SDL_DisplayMode mode;
SDL_GetDisplayMode(0,0,&mode);
int width = mode.w;
int height = mode.h;

window = SDL_CreateWindow(
            title,
            0,
            0,
            width,
            height,
            SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_RESIZABLE
        );

SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES );

Sur l’émulateur, l’application est installée par défaut avec l’icône SDL et le nom « Jeu ».

Il me reste juste à explorer la possibilité de générer automatiquement des fichiers d’assembly basés sur CMake, ou de migrer des assemblys pour toutes les plateformes vers Gradle ; cependant, CMake reste le choix de facto pour le développement C++ en cours.

Code source

https://gitlab.com/demensdeum/android- sdl-test-app
https://gitlab.com/demensdeum/android-sdl-test-app/tree/master/cube-art-project-android

Sources

https://developer.android.com/ ndk/guides/cpp-support.html
https://developer.android.com/ndk/guides/application_mk
https://developer.android.com/ndk/guides/android_mk
https://lazyfoo.net/tutorials/SDL/52_hello_mobile/android_windows/index.php
https://medium.com/androiddevelopers/getting-started-with-c-and-android-native-activities-2213b402ffff

SDL2 – OpenGL ES

I love Panda3D game engine. But right now this engine is very hard to compile and debug on Microsoft Windows operation system. So as I said some time ago, I begin to develop my own graphics library. Right now it’s based on OpenGL ES and SDL2.
In this article I am going to tell how to initialize OpenGL ES context and how SDL2 helps in this task. We are going to show nothing.

King Nothing

First of all you need to install OpenGL ES3 – GLES 3 libraries. This operation is platform dependant, for Ubuntu Linux you can just type sudo apt-get install libgles2-mesa-dev. To work with OpenGL you need to initialize OpenGL context. There is many ways to do that, by using one of libraries – SDL2, GLFW, GLFM etc. Actually there is no one right way to initialize OpenGL context, but I chose SDL2 because it’s cross-platform solution, code will look same for Windows/*nix/HTML5/iOS/Android/etc.

To install sdl2 on Ubuntu use this command sudo apt-get install libsdl2-dev

So here is OpenGL context initialization code with SDL2:

    SDL_Window *window = SDL_CreateWindow(
            "SDL2 - OGLES",
            SDL_WINDOWPOS_UNDEFINED,
            SDL_WINDOWPOS_UNDEFINED,
            640,
            480,
            SDL_WINDOW_OPENGL
            );
	    

    SDL_GLContext glContext = SDL_GL_CreateContext(window);

After that, you can use any OpenGL calls in that context.

Here is example code for this article:
https://github.com/demensdeum/OpenGLES3-Experiments/tree/master/3sdl-gles
https://github.com/demensdeum/OpenGLES3-Experiments/blob/master/3sdl-gles/sdlgles.cpp

You can build and test it with command cmake . && make && ./SDLGles