Skip to content

Working for Android (on macOS)

Ferdinand E. Silva edited this page Dec 2, 2024 · 7 revisions

Building raylib for Android on MAC OSX

It's helpful to know step by step what each part of the process is doing. For this, let's create a simple bash script, where each line can be run manually and the results can be seen. This should be useful for understanding the process on Linux as well.

This is simple as in we're just using shell commands, no cmake, no builders of any kind.

Initial folder setup

Note: project.c is copied from raylib/examples/examples_template.c

/raylib                 put raylib here
/project1/lib
/project1/obj
/project1/src
/project1/dex
/project1/res/values/strings.xml
/project1/res/drawable-hdpi
/project1/res/drawable-ldpi
/project1/res/drawable-mdpi
/project1/res/drawable-xhdpi
/project1/assets
/project1/project.c
/project1/AndroidManifest.xml

/res/values/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">My App</string>
</resources>

/src/com/seth/project/NativeLoader.java

Note: My project is named "project" and the namespace is named "seth"

package com.seth.project; 
public class NativeLoader extends android.app.NativeActivity { 
    static {
        System.loadLibrary("project"); // must match name of shared library (in this case libproject.so) 
    } 
} 

AndroidManifest.xml

Note: "package" is the unique name of your project, this will overwrite apps with the same name

<?xml version='1.0' encoding="utf-8" ?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package='com.seth.project' android:versionCode='0'
    android:versionName='0' >
    <uses-sdk
        android:minSdkVersion="23"
        android:targetSdkVersion="30" />
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name="com.seth.project.NativeLoader"
            android:screenOrientation="landscape"
            android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
            android:configChanges="orientation|keyboardHidden|screenSize"
            android:launchMode="singleTask"
            android:clearTaskOnLaunch="true">
        <meta-data android:name="android.app.lib_name" android:value="project"/>
            <intent-filter>
                <category android:name='android.intent.category.LAUNCHER'/>
                <action android:name='android.intent.action.MAIN'/>
            </intent-filter>
        </activity>
    </application>
</manifest>

Outline of the Process

cp              Copy libs from raylib to lib and copy icons from raylib/logo to res
clang           Gen native_app_glue.o from raylib
llvm-ar rcs     Gen obj/libnative_app_glue.a from native_app_glue.o
clang           Gen project.o from project.c
clang           Gen lib/libproject.so from obj/project.o
aapt package    Gen R.java
javac           Gen classes from R.java and NativeLoader.java
zip             Gen zip file from obj directory
d8              Gen classes.dex from objects inside zip file (Replaced dx)
unzip           Extract classes.dex file
mv              Move classes.dex file to dex directory
aapt package    Gen project.unsigned.apk
aapt add        Add shared library to project.unsigned.apk
keytool         Generate keystore
zipalign        Align project
apksigner       Gen project.apk (Replaced jarsigner)
adb install     Install project
adb logcat      view log

Install Standalone Toolchain

Install ndk-bundle using Android Studio.

Install raylib

Compile raylib into ./raylib

~/.zprofile

Add these files to path so you won't have a big headache later on

export ANDROID_SDK_ROOT=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_SDK_ROOT/emulator
export PATH=$PATH:$ANDROID_SDK_ROOT/build-tools/36.0.0-rc1
export PATH=$PATH:$ANDROID_SDK_ROOT/tools
export PATH=$PATH:$ANDROID_SDK_ROOT/tools/bin
export PATH=$PATH:$ANDROID_SDK_ROOT/platform-tools
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/28.0.12674087
export PATH=$PATH:$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/darwin-x86_64/bin

Initial Project Setup

Note: Keytool is in java folder on system

cd project1
mkdir lib/arm64-v8a
cp ../raylib/src/libraylib.a lib/arm64-v8a/libraylib.a
cp ../raylib/logo/raylib_72x72.png res/drawable-hdpi/icon.png 
cp ../raylib/logo/raylib_36x36.png res/drawable-ldpi/icon.png 
cp ../raylib/logo/raylib_48x48.png res/drawable-mdpi/icon.png 
cp ../raylib/logo/raylib_96x96.png res/drawable-xhdpi/icon.png 
keytool -genkeypair -validity 1000 -dname "CN=seth,O=Android,C=ES" -keystore project.keystore -storepass 'mypass' -keypass 'mypass' -alias projectKey -keyalg RSA

Build Script

Note: Here's step by step commands which lead to the installation of your project!

aarch64-linux-android29-clang -c ../raylib/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/android_native_app_glue.c -o obj/native_app_glue.o -std=c99 -march=armv8-a -mfix-cortex-a53-835769 -ffunction-sections -funwind-tables -fstack-protector-strong -fPIC -Wall -Wa,--noexecstack -Wformat -Werror=format-security -no-canonical-prefixes -DANDROID -DPLATFORM_ANDROID -D__ANDROID_API__=29

# Requires: folder setup
# Creates: obj/native_app_glue.o

llvm-ar rcs obj/libnative_app_glue.a obj/native_app_glue.o

# Requires: obj/native_app_glue.o
# Creates: obj/libnative_app_glue.a

aarch64-linux-android29-clang -c project.c -o obj/project.o -I. -I../raylib/src -I../raylib/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/ -std=c99 -march=armv8-a -mfix-cortex-a53-835769 -ffunction-sections -funwind-tables -fstack-protector-strong -fPIC -Wall -Wa,--noexecstack -Wformat -Werror=format-security -no-canonical-prefixes -DANDROID -DPLATFORM_ANDROID -D__ANDROID_API__=29 --sysroot=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/darwin-x86_64/sysroot

# Requires: project.c
# Creates: obj/project.o

aarch64-linux-android29-clang -o lib/arm64-v8a/libproject.so obj/project.o -shared -I. -I../raylib/src -I../raylib/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity -Wl,-soname,libproject.so -Wl,--exclude-libs,libatomic.a -Wl,--build-id -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel -Wl,--fatal-warnings -u ANativeActivity_onCreate -L. -Lobj -Llib/arm64-v8a -lraylib -lnative_app_glue -llog -landroid -lEGL -lGLESv2 -lOpenSLES -latomic -lc -lm -ldl

# Requires: obj/project.o
# Creates: lib/arm64-v8a/libproject.so

aapt package -f -m -S res -J src -M AndroidManifest.xml -I ${ANDROID_SDK_ROOT}/platforms/android-29/android.jar

# Requires: AndroidManifest.xml, res/
# Creates: src/com/seth/project/R.java

javac -verbose -source 1.7 -target 1.7 -d obj -bootclasspath `/usr/libexec/java_home`/jre/lib/rt.jar -classpath ${ANDROID_SDK_ROOT}/platforms/android-29/android.jar:obj -sourcepath src src/com/seth/project/R.java src/com/seth/project/NativeLoader.java

# Requires: src/com/seth/project/R.java, src/com/seth/project/NativeLoader.java
# Creates: obj/com/seth/project/NativeLoader.class ... R&attr.class R$string.class R.class 

zip -r obj.zip obj
d8 obj.zip --output obj.zip
unzip obj.zip
mv classes.dex dex/

# Requires: obj/com/seth/project/NativeLoader.class ... R&attr.class R$string.class R.class 
# Creates: dex/classes.dex

aapt package -f -M AndroidManifest.xml -S res -A assets -I ${ANDROID_SDK_ROOT}/platforms/android-29/android.jar -F project.unsigned.apk dex

# Creates: project.unsigned.apk
# Note: The "dex" at the end is the directory the classes.dex file is in! This folder can not contain the manifest file for whatever reason.

aapt add project.unsigned.apk lib/arm64-v8a/libproject.so

# Does: Adds shared library to apk

zipalign -v 4 project.unsigned.apk project.unsigned-align.apk

# Does: Aligns

apksigner sign --ks project.keystore --ks-pass pass:mypass --key-pass pass:mypass --ks-key-alias projectKey --out project.apk project.unsigned-align.apk

# Does: Signs

adb install -r project.apk

# Does: install

Dealing with Segmentation Faults - Using adb logcat

In the event of a segfault, don't despair! You can get the entire backtrace using adb logcat.

adb logcat "libc:*" "DEBUG:*" "*:S"

This gives you everything tagged with libc or DEBUG. "*:S" is there to Silence all other stuff.

Now, you may see a message that says "Suppressing debuggerd output because prctl(PR_GET_DUMPABLE)==0"

In that case you have to set this to 1.

In your c code, dd this include statement, and the prctl command somewhere early on in your main():

#include <sys/prctl.h>
...
prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);

Here's a more robust logcat command:

adb logcat "threaded_app:*" "raylib:*" "libc:*" "DEBUG:*" "InputDispatcher:*" "JavaBinder:*" "WindowState:*" "Eve:*" "ViewRootImpl:*" "*:S"

Or be brave and try to find the important tags yourself:

adb logcat "*:*"
Clone this wiki locally