Update guide for modern SDK versions

gtrxAC
2022-07-20 17:58:24 +03:00
parent f36336a559
commit 9fb9bea442

@ -1,172 +1,277 @@
## NOTE: This guide is tested on Ubuntu 18.04 and Android 6.0.1
# Installing required tools
***
This guide shows you how to set up and build a raylib project for Android on Linux. This was tested on Ubuntu 21.10 (64-bit only) and Android 10. Generated APK files will run on Android 6 and up.
We need a set of tools in order to generate an .apk file. These tools are nothing more than couple things. Build process is relatively simple. It is done by shell commands and Android tools.
## 1- Java 8 JDK
You can get Java 8 JDK in multiple ways. I'm using OpenJDK at this guide. Follow the instructions at https://openjdk.java.net/ address to install OpenJDK.
## 2- Android SDK
Android SDK has many packages. If you want to download all of them, you can go install [Android Studio](https://developer.android.com/studio/) and install the full package. But if you don't, you can download only the required ones manually by [Command Line Tools](https://developer.android.com/studio/#command-tools). To do that, start downloading the [Command Line Tools](https://developer.android.com/studio/#command-tools).
* Create an empty folder named `android-sdk`.
* Decompress downloaded `sdk-tools-linux-...` into just created folder `android-sdk`.
* From the Terminal, navigate to created `android-sdk` folder and run the following commands:
# Setup
## Directory setup
First, we need to create some directories for files used in the build process. Open a terminal in your project folder, this terminal will be needed throughout the guide. Run the following commands:
```sh
mkdir --parents android/sdk android/build assets include lib/armeabi-v7a lib/arm64-v8a lib/x86 lib/x86_64 src
cd android/build
mkdir --parents obj dex res/values src/com/raylib/game assets
mkdir --parents lib/armeabi-v7a lib/arm64-v8a lib/x86 lib/x86_64
mkdir --parents res/drawable-ldpi res/drawable-mdpi res/drawable-hdpi res/drawable-xhdpi
cd ../..
```
mv -v cmdline-tools tools
export ANDROID_SDK=$(pwd)
cd tools/bin
./sdkmanager --update --sdk_root=$ANDROID_SDK
./sdkmanager --list --sdk_root=$ANDROID_SDK
./sdkmanager --install "build-tools;28.0.1" --sdk_root=$ANDROID_SDK
./sdkmanager --install platform-tools --sdk_root=$ANDROID_SDK
./sdkmanager --install "platforms;android-22" --sdk_root=$ANDROID_SDK
unset ANDROID_SDK
* `android` contains everything used by the Android build system.
* `android/ndk` contains the Android NDK.
* `android/sdk` contains the SDK and all of its packages.
* `android/build` contains temporary files used for the build, these will be packaged into the APK file.
* `assets` is used for your app icon and any other assets your project may need.
* `include` only has `raylib.h` by default but you can put headers for other libraries if needed.
* `lib` contains the compiled raylib library for Android.
* `src` contains your app's source code.
## Java
Android is based on the Java language, a Java installation is also required to create Android apps. There are many versions of Java, I use OpenJDK version 18 (or newer) which can be downloaded [here](https://openjdk.java.net/).
## Android SDK
The Android SDK (Software Development Kit) lets you create apps for Android. It has a modular design. The [Android Studio](https://developer.android.com/studio/) contains all of the packages, but in this guide we'll use the command line tools to install only the necessary packages. You can get the command line tools [here](https://developer.android.com/studio/#command-tools).
1. Decompress downloaded `sdk-tools-linux-...` into your project in `android/sdk`. Make sure there is a `cmdline-tools` folder.
2. From the terminal, run the following commands:
```sh
cd android/sdk/cmdline-tools/bin
./sdkmanager --update --sdk_root=../..
./sdkmanager --install "build-tools;29.0.3" --sdk_root=../..
./sdkmanager --install "platform-tools" --sdk_root=../..
./sdkmanager --install "platforms;android-29" --sdk_root=../..
cd ../../../..
```
All required tools will be downloaded by these commands. You can check out package's description by looking at `./sdkmanager --list`.
## 3- Android NDK
Raylib is written in C, hence we need Android NDK. Android Native Development Kit (NDK) allows us to develop in C. You can download it from [here](https://developer.android.com/ndk/downloads/). In this case we need Android NDK because we are going to use it's compiler and API with a standalone toolchain.
After downloading Android NDK
* Decompress downloaded `android-ndk-...` and rename it as `android-ndk`
* From Terminal navigate to `android-ndk/build/tools`
* Run `python make_standalone_toolchain.py --arch arm --api 22 --install-dir=android_toolchain_ARM_API22`
# Compiling Raylib
***
## Android NDK
The Android NDK (Native Development Kit) allows Android apps to use the C programming language. Download it [here](https://developer.android.com/ndk/downloads/) and extract it. Rename the created `android-ndk-r...` folder to `ndk` and put it inside the `android` folder. Make sure there are `build`, `meta`, etc. folders inside `android/ndk`.
First you should download Raylib source code. Run following command from Terminal:
`git clone https://github.com/raysan5/raylib.git raylib`
Source code will be downloaded to a directory named raylib.
We need to compile Raylib with Android NDK Standalone Toolchain that we made in the previous section.
Don't forget to setup your `ANDROID_TOOLCHAIN` path and `ANDROID_NDK` path in your Makefile!(`raylib/src/Makefile`)
Default property for `ANDROID_TOOLCHAIN` is `ANDROID_TOOLCHAIN = C:/android_toolchain_$(ANDROID_ARCH)_API$(ANDROID_API_VERSION)`
Default property for `ANDROID_NDK` is `ANDROID_NDK = C:/android-ndk`
After that navigate from Terminal to `raylib/src` and compile Raylib with `make PLATFORM=PLATFORM_ANDROID`
This command will create `libraylib.a` in `/src`.
# Initial Setup For Your Raylib app
***
## Folders
Actually, we can add this part to a `.sh` file but to clarify building process, we are going to create all necessary files manually.
> Note: project.c is copied from `raylib/templates/simple_game/simple_game.c`
## Compile raylib
If you don't already have `raylib` in your project folder, clone the repository.
```sh
git clone https://github.com/raysan5/raylib --depth 1
```
/toolchain_arm_api22 put android_toolchain_arm_api22 here
/raylib put raylib here
/project/lib
/project/obj
/project/src
/project/dex
/project/res/values/strings.xml
/project/assets
/project/project.c
/project/AndroidManifest.xml
Build raylib for ARM and x86 architectures, both 32 and 64 bit. You can exclude some of these if you know what you're doing. Make sure you don't see any errors and that the file sizes in `lib` make sense for each architecture.
```sh
cd raylib/src
cp raylib.h ../../include
make clean
make PLATFORM=PLATFORM_ANDROID ANDROID_NDK=../../android/ndk ANDROID_ARCH=arm ANDROID_API_VERSION=29
mv libraylib.a ../../lib/armeabi-v7a
make clean
make PLATFORM=PLATFORM_ANDROID ANDROID_NDK=../../android/ndk ANDROID_ARCH=arm64 ANDROID_API_VERSION=29
mv libraylib.a ../../lib/arm64-v8a
make clean
make PLATFORM=PLATFORM_ANDROID ANDROID_NDK=../../android/ndk ANDROID_ARCH=x86 ANDROID_API_VERSION=29
mv libraylib.a ../../lib/x86
make clean
make PLATFORM=PLATFORM_ANDROID ANDROID_NDK=../../android/ndk ANDROID_ARCH=x86_64 ANDROID_API_VERSION=29
mv libraylib.a ../../lib/x86_64
make clean
cd ../..
```
## Files
### project/res/values/strings.xml
## Prepare project
A few more files need to be created for the Android build to work.
1. Create icons for different display densities (ldpi, mdpi, hdpi, xhdpi). In this guide, I will put them in `assets`, named `icon_ldpi.png`, `icon_mdpi.png` and so on. There are higher display densities but icons for them are not needed. For example, these commands create the icons for the raylib logo:
```sh
cp raylib/logo/raylib_36x36.png assets/icon_ldpi.png
cp raylib/logo/raylib_48x48.png assets/icon_mdpi.png
cp raylib/logo/raylib_72x72.png assets/icon_hdpi.png
cp raylib/logo/raylib_96x96.png assets/icon_xhdpi.png
```
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">My App</string>
</resources>
2. Keystore, this file contains the key for signing your APK file. You can change the `storepass` and `keypass` if you want to.
```sh
cd android
keytool -genkeypair -validity 1000 -dname "CN=raylib,O=Android,C=ES" -keystore raylib.keystore -storepass 'raylib' -keypass 'raylib' -alias projectKey -keyalg RSA
cd ..
```
### /src/com/raylib_app/project/NativeLoader.java
> Note: My project name is `project` and my namespace is named `raylib_app`. You can change it however you want.
```
package com.raylib_app.project;
public class NativeLoader extends android.app.NativeActivity {
3. We still need a tiny bit of Java code to launch our app, this is handled by the NativeLoader class. Save this file in `android/build/src/com/raylib/game/NativeLoader.java`.
```java
package com.raylib.game;
public class NativeLoader extends android.app.NativeActivity {
static {
System.loadLibrary("project"); // must match name of shared library (in this case libproject.so)
}
}
System.loadLibrary("main");
}
}
```
### AndroidManifest.xml
```
<?xml version='1.0' encoding="utf-8" ?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package='com.raylib_app.project' android:versionCode='0'
android:versionName='0' >
<application>
<activity android:name="com.raylib_app.project.NativeLoader"
4. The last file is our manifest, this file contains metadata about the app such as name and version. Save this in `android/build/AndroidManifest.xml`. There are a few things you can change here:
* `android:label="Game"` This will be the app name in the launcher (home screen).
* `android:versionCode="1"` Internal version code, this is not seen by the user.
* `android:versionName="1.0"` Human readable version number.
* `android:screenOrientation="landscape"` You can change this to `portrait` if needed.
```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.raylib.game"
android:versionCode="1" android:versionName="1.0" >
<uses-sdk android:minSdkVersion="23" android:targetSdkVersion="29"/>
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application android:allowBackup="false" android:label="Game" android:icon="@drawable/icon">
<activity android:name="com.raylib.game.NativeLoader"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:configChanges="orientation|keyboardHidden|screenSize"
android:launchMode="singleTask"
android:screenOrientation="landscape" android:launchMode="singleTask"
android:clearTaskOnLaunch="true">
<meta-data android:name="android.app.lib_name" android:value="project"/>
<meta-data android:name="android.app.lib_name" android:value="main"/>
<intent-filter>
<category android:name='android.intent.category.LAUNCHER'/>
<action android:name='android.intent.action.MAIN'/>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
<supports-screens android:anyDensity="true"/>
</application>
</manifest>
```
### lib/armeabi-v7a/libraylib.a
# Build
Okay, finally we can build our APK file. You can create a shell script to build your project faster. Name it for example `build.sh` or `build_android.sh` in your project folder.
```sh
#!/bin/sh
# ______________________________________________________________________________
#
# Compile raylib project for Android
# ______________________________________________________________________________
#
# NOTE: If you excluded any ABIs in the previous steps, remove them from this list too
# TODO: arm64-v8a building doesn't work:
# /usr/bin/ld: /tmp/main-08f12a.o: Relocations in generic ELF (EM: 183)
# /usr/bin/ld: /tmp/main-08f12a.o: Relocations in generic ELF (EM: 183)
# /usr/bin/ld: /tmp/main-08f12a.o: error adding symbols: file in wrong format
ABIS="armeabi-v7a x86 x86_64"
BUILD_TOOLS=android/sdk/build-tools/29.0.3
TOOLCHAIN=android/ndk/toolchains/llvm/prebuilt/linux-x86_64
NATIVE_APP_GLUE=android/ndk/sources/android/native_app_glue
FLAGS="-ffunction-sections -funwind-tables -fstack-protector-strong -fPIC -Wall \
-Wformat -Werror=format-security -no-canonical-prefixes \
-DANDROID -DPLATFORM_ANDROID -D__ANDROID_API__=29"
INCLUDES="-I. -Iinclude -I../include -I$NATIVE_APP_GLUE -I$TOOLCHAIN/sysroot/usr/include"
# Copy icons
cp assets/icon_ldpi.png android/build/res/drawable-ldpi/icon.png
cp assets/icon_mdpi.png android/build/res/drawable-mdpi/icon.png
cp assets/icon_hdpi.png android/build/res/drawable-hdpi/icon.png
cp assets/icon_xhdpi.png android/build/res/drawable-xhdpi/icon.png
# Copy other assets
cp assets/* android/build/assets
# ______________________________________________________________________________
#
# Compile
# ______________________________________________________________________________
#
for ABI in $ABIS; do
case "$ABI" in
"armeabi-v7a")
CCTYPE="armv7a-linux-androideabi"
ABI_FLAGS="-std=c99 -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16"
;;
"arm64-v8a")
CCTYPE="aarch64-linux-android"
ABI_FLAGS="-std=c99 -target aarch64 -mfix-cortex-a53-835769"
;;
"x86")
CCTYPE="i686-linux-android"
ABI_FLAGS=""
;;
"x86_64")
CCTYPE="x86_64-linux-android"
ABI_FLAGS=""
;;
esac
CC="$TOOLCHAIN/bin/${CCTYPE}29-clang"
# Compile native app glue
# .c -> .o
$CC -c $NATIVE_APP_GLUE/android_native_app_glue.c -o $NATIVE_APP_GLUE/native_app_glue.o \
$INCLUDES -I$TOOLCHAIN/sysroot/usr/include/$CCTYPE $FLAGS $ABI_FLAGS
# .o -> .a
$TOOLCHAIN/bin/llvm-ar rcs lib/$ABI/libnative_app_glue.a $NATIVE_APP_GLUE/native_app_glue.o
# Compile project
$CC src/*.c -o android/build/lib/$ABI/libmain.so -shared \
$INCLUDES -I$TOOLCHAIN/sysroot/usr/include/$CCTYPE $FLAGS $ABI_FLAGS \
-Wl,-soname,libmain.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. -Landroid/build/obj -Llib/$ABI \
-lraylib -lnative_app_glue -llog -landroid -lEGL -lGLESv2 -lOpenSLES -latomic -lc -lm -ldl
done
# ______________________________________________________________________________
#
# Build APK
# ______________________________________________________________________________
#
$BUILD_TOOLS/aapt package -f -m \
-S android/build/res -J android/build/src -M android/build/AndroidManifest.xml \
-I android/sdk/platforms/android-29/android.jar
# Compile NativeLoader.java
javac -verbose -source 1.8 -target 1.8 -d android/build/obj \
-bootclasspath jre/lib/rt.jar \
-classpath android/sdk/platforms/android-29/android.jar:android/build/obj \
-sourcepath src android/build/src/com/raylib/game/R.java \
android/build/src/com/raylib/game/NativeLoader.java
$BUILD_TOOLS/dx --verbose --dex --output=android/build/dex/classes.dex android/build/obj
# Add resources and assets to APK
$BUILD_TOOLS/aapt package -f \
-M android/build/AndroidManifest.xml -S android/build/res -A assets \
-I android/sdk/platforms/android-29/android.jar -F game.apk android/build/dex
# Add libraries to APK
cd android/build
for ABI in $ABIS; do
../../$BUILD_TOOLS/aapt add ../../game.apk lib/$ABI/libmain.so
done
cd ../..
# Sign and zipalign APK
# NOTE: If you changed the storepass and keypass in the setup process, change them here too
jarsigner -keystore android/raylib.keystore -storepass raylib -keypass raylib \
-signedjar game.apk game.apk projectKey
$BUILD_TOOLS/zipalign -f 4 game.apk game.final.apk
mv -f game.final.apk game.apk
# Install to device or emulator
android/sdk/platform-tools/adb install -r game.apk
```
cd project
mkdir lib/armeabi-v7a
cp ../raylib/src/libraylib.a lib/armeabi-v7a/libraylib.a
keytool -genkeypair -validity 1000 -dname "CN=seth,O=Android,C=ES" -keystore project.keystore -storepass 'whatever' -keypass 'whatever' -alias projectKey -keyalg RSA
```
# Build Script
```
../android_toolchain_ARM_API22/bin/arm-linux-androideabi-gcc -c ../raylib/src/external/android/native_app_glue/android_native_app_glue.c -o obj/native_app_glue.o -std=c99 -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -ffunction-sections -funwind-tables -fstack-protector-strong -fPIC -Wall -Wa,--noexecstack -Wformat -Werror=format-security -no-canonical-prefixes -DANDROID -DPLATFORM_ANDROID -D__ANDROID_API__=22
# Requires: folder setup
# Creates: obj/native_app_glue.o
# Note: This gcc uses other tools in the same toolchain folder structure, don't even thing about symlinking to it.
# Improvements
raylib android support could still be improved, there are a few issues:
../android_toolchain_ARM_API22/bin/arm-linux-androideabi-ar rcs obj/libnative_app_glue.a obj/native_app_glue.o
## File system
Currently the file system is not accessible on Android. Trying to load any file outside the assets folder results in `Failed to open file`. This means that any user created files cannot be loaded.
# Requires: obj/native_app_glue.o
# Creates: obj/libnative_app_glue.a
Assets such as images and sounds that are bundled into the APK file can still be loaded. This build system automatically bundles everything in the `assets` folder into the APK.
../android_toolchain_ARM_API22/bin/arm-linux-androideabi-gcc -c project.c -o obj/project.o -I. -I../raylib/release/include -I../raylib/src/external/android/native_app_glue -std=c99 -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -ffunction-sections -funwind-tables -fstack-protector-strong -fPIC -Wall -Wa,--noexecstack -Wformat -Werror=format-security -no-canonical-prefixes -DANDROID -DPLATFORM_ANDROID -D__ANDROID_API__=22 --sysroot=../toolchain_ARM_API22/sysroot
## Assets folder
On Android, the default working directory for loading assets is the `assets` folder. On other platforms, it is usually the directory containing the executable. To make sure your assets are loaded from the same directory on all platforms, you can do this:
# Requires: project.c
# Creates: obj/project.o
```c
#ifndef PLATFORM_ANDROID
ChangeDirectory("assets");
#endif
../android_toolchain_ARM_API22/bin/arm-linux-androideabi-gcc -o lib/armeabi-v7a/libproject.so obj/project.o -shared -I. -I../raylib/release/include -I../raylib/src/external/android/native_app_glue -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/armeabi-v7a -lraylib -lnative_app_glue -llog -landroid -lEGL -lGLESv2 -lOpenSLES -latomic -lc -lm -ldl
myTexture = LoadTexture("texture.png");
mySound = LoadSound("sound.wav");
myFont = LoadFont("font.ttf");
# Requires: obj/project.o
# Creates: lib/armeabi-v7a/libproject.so
ANDROID_SDK_PATH/build-tools/28.0.1/aapt package -f -m -S res -J src -M AndroidManifest.xml -I ANDROID_SDK_PATH/platforms/android-22/android.jar
# Requires: AndroidManifest.xml, res/
# Creates: src/com/raylib_app/project/R.java
javac -verbose -source 1.7 -target 1.7 -d obj -bootclasspath /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jre/lib/rt.jar -classpath ANDROID_SDK_PATH/platforms/android-22/android.jar:obj -sourcepath src src/com/raylib_app/project/R.java src/com/raylib_app/project/NativeLoader.java
# Requires: src/com/raylib_app/project/R.java, src/com/raylib_app/project/NativeLoader.java
# Creates: obj/com/raylib_app/project/NativeLoader.class ... R&attr.class R$string.class R.class
ANDROID_SDK_PATH/build-tools/28.0.1/dx --verbose --dex --output=dex/classes.dex obj
# Requires: obj/com/raylib_app/project/NativeLoader.class ... R&attr.class R$string.class R.class
# Creates: dex/classes.dex
ANDROID_SDK_PATH/build-tools/28.0.1/aapt package -f -M AndroidManifest.xml -S res -A assets -I ANDROID_SDK_PATH/platforms/android-22/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.
ANDROID_SDK_PATH/build-tools/28.0.1/aapt add project.unsigned.apk lib/armeabi-v7a/libproject.so
# Does: Adds shared library to apk
jarsigner -keystore project.keystore -storepass whatever -keypass whatever -signedjar project.signed.apk project.unsigned.apk projectKey
# Does: Signs
ANDROID_SDK_PATH/build-tools/28.0.1/zipalign -f 4 project.signed.apk project.apk
# Does: Aligns
ANDROID_SDK_PATH/platform-tools/adb install -r project.apk
# Does: install (on an Android phone that you connect with USB Debugging)
```
It is all done here. You can change the build script as your preferences. Have fun!
#ifndef PLATFORM_ANDROID
ChangeDirectory("..");
#endif
```